初识Spring AOP

简介: 初识Spring AOP

概念


AOP介绍


AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,,这时候 Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理,如下图所示:


image.png


除此之外还有一种情况,如果被代理对象实现了接口,可以强制使用 Cglib 实现 AOP。

当然你也可以使用 AspectJ ,Spring AOP 已经集成了AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。Spring AOP 基于注解配置的情况下,需要依赖于 AspectJ 包的标准注解,但是不需要额外的编译以及 AspectJ 的织入器,而基于 XML 配置不需要,所以 Spring AOP 只是复用了 AspectJ 的注解,并没有其他依赖 AspectJ 的地方。


使用 AOP 之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样大大简化了代码量。我们需要增加新功能时也方便,这样也提高了系统扩展性。日志功能、事务管理等等场景都用到了 AOP 。


当 Spring 需要使用 @AspectJ 注解支持时,需要在 Spring 配置文件中如下配置:


<aop:aspectj-autoproxy/>
复制代码


而关于强制使用 Cglib,可以通过在 Spring 配置文件如下实现:


<aop:aspectj-autoproxy proxy-target-class="true"/>
复制代码


proxy-target-class 属性值决定是基于接口的还是基于类的代理被创建,默认为 false。如果 proxy-target-class 属性值被设置为 true,那么基于类的代理将有效(这时需要 Cglib 库)。反之是基于接口的代理(JDK的动态代理)。


所以,虽然使用了 AspectJ 的 Annotation,但是并没有使用它的编译器和织入器。其实现原理是 JDK 动态代理或 Cglib,在运行时生成代理类。


AOP核心概念


连接点:JoinPoint,就是要被拦截的方法

切入点:PointCut,定义在哪些类,哪些方法上切入(拦截)

通知:Advice,在拦截到连接点之后要执行的代码,通知主要有五种,前置通知,后置通知,异常通知,最终通知和环绕通知。

切面:AspectJ,就是切入点加上通知

织入:weaving,把切面加入对象,并创建出代理对象的过程。


AOP标签配置元素


aop:aspect: 定义一个切面

aop:before: 定义一个前置通知

aop:after: 定义一个后置通知

aop:around: 定义一个环绕通知

aop:after-returning : 定义一个返回通知

aop:after-throwing :定义一个异常通知

aop:config: 顶层的aop配置元素

aop:pointcut: 定义切入点


AOP实现步骤

  1. 定义普通的业务组件;
  2. 定义切入点;
  3. 定义增强处理,也就是在AOP框架中为普通的业务逻辑织入的处理动作。


实例


基于XML


继续使用租房的案例,接下来新建一个切面类:

public class ProxyXML {
    public void seeHouse(){
        System.out.println("带客户看房子");
    }
    public Object getMoney(ProceedingJoinPoint p){
        System.out.println("租房.....before");
        Object o = null;
        try{
            o = p.proceed();
        }catch(Throwable e){
            e.printStackTrace();
        }
        System.out.println("租房.....after");
        return o;
    }
    public void fare(){
        System.out.println("收取中介费");
    }
}
复制代码


配置文件:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="host" class="com.msdn.bean.Host" />
    <bean id="proxy" class="com.msdn.aop.ProxyXML" />
    <!--Spring基于Xml的切面-->
    <aop:config>
        <!--定义切点函数-->
        <aop:pointcut id="rentPointCut" expression="execution(* com.msdn.bean.Host.rent())"/>
        <!-- 定义切面 order 定义优先级,值越小优先级越大-->
        <aop:aspect ref="proxy" order="0">
            <!--前置通知-->
            <aop:before method="seeHouse" pointcut-ref="rentPointCut" />
            <!--环绕通知-->
            <aop:around method="getMoney" pointcut-ref="rentPointCut" />
            <!--后置通知-->
            <aop:after method="fare" pointcut-ref="rentPointCut" />
        </aop:aspect>
    </aop:config>
</beans>
复制代码


pom.xml:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring_study</artifactId>
        <groupId>com.msdn</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>spring-chap3</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.2</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjtools</artifactId>
            <version>1.9.2</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.2</version>
        </dependency>
    </dependencies>
</project>
复制代码


测试代码:


@Test
public void aopTest(){
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml");
    Object host = context.getBean("host");
    Rent o = (Rent) host;
    o.rent();
}
复制代码


执行结果为:


带客户看房子
租房.....before
房屋出租
租房.....after
收取中介费
复制代码


基于注解


切面类:


@Order(0)
@Aspect
@Component
public class ProxyAnnotation {
    @Before(value = "execution(* com.msdn.bean.Host.rent())")
    public void seeHouse(){
        System.out.println("带客户看房子");
    }
    @Around(value = "execution(* com.msdn.bean.Host.rent())")
    public Object getMoney(ProceedingJoinPoint p){
        System.out.println("租房.....before");
        Object o = null;
        try{
            o = p.proceed();
        }catch(Throwable e){
            e.printStackTrace();
        }
        System.out.println("租房.....after");
        return o;
    }
    @After(value = "execution(* com.msdn.bean.Host.rent())")
    public void fare(){
        System.out.println("收取中介费");
    }
}
复制代码


切面类版本二:


@Order(0)
@Aspect
@Component
public class ProxyAnnotation2 {
    @Pointcut("execution(* com.msdn.bean.Host.rent())")
    public void rentPointCut(){
    }
    @Before(value = "rentPointCut()")
    public void seeHouse(){
        System.out.println("带客户看房子");
    }
    @Around(value = "rentPointCut()")
    public Object getMoney(ProceedingJoinPoint p){
        System.out.println("租房.....before,优先级更高");
        Object o = null;
        try{
            o = p.proceed();
        }catch(Throwable e){
            e.printStackTrace();
        }
        System.out.println("租房.....after");
        return o;
    }
    @After(value = "rentPointCut()")
    public void fare(){
        System.out.println("收取中介费");
    }
}
复制代码


配置文件:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <context:component-scan base-package="com.msdn.bean,com.msdn.aop" />
    <aop:aspectj-autoproxy proxy-target-class="true" />
</beans>
复制代码


测试代码:


@Test
    public void aopTest(){
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop2.xml");
        Object host = context.getBean("host");
        Rent o = (Rent) host;
        o.rent();
    }
复制代码


执行结果为:


租房.....before
带客户看房子
房屋出租
租房.....after
收取中介费



目录
相关文章
|
9天前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
47 8
|
2月前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
2月前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
95 5
|
2月前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
89 8
|
2月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
2月前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
53 5
|
2月前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
54 4
|
3月前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
60 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
2月前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
51 1
|
4月前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP