Spring AOP

AOP是一种编程范式,用于减少代码重复,提高可维护性。SpringAOP是其实现,通过切面、切点和通知实现动态功能植入。文章介绍了如何定义切面和切点,以及使用@Before、@After等注解定义通知,同时讲解了SpringAOP的动态代理机制,包括JDK和CGLib两种方式。
摘要由CSDN通过智能技术生成

目录

AOP

理解AOP

AOP组成

AOP的优点

Spring AOP

使用Spring AOP

定义切面和切点

定义通知

动态代理 

织入


AOP

理解AOP

AOP即面向切面编程,简单来说,就是把一部分通用的功能集中的放在一个地方处理的思想。假如某一段代码很多地方要用到,比如说登录验证,在传统编程中,有两个做法,要么每次复制粘贴,要么把他封装成一个函数再调用。复制粘贴显然是最坏的一种做法,一旦涉及到修改就会很麻烦。因此封装成函数是一种更优的做法。而AOP可以看作是一种更为高级和抽象的封装,它可以动态地植入通用功能。Spring AOP是AOP思想的一种具体实现。

AOP组成

AOP的基本组成包含切面(Aspect)、切点(Pointcut)、连接点(Joinpoint)、通知(Advice)。

  • 切面(Aspect):由连接点、切点和通知组成。简单来说切面就是一个包含了连接点、切点和通知的类。即实现某个或某些功能的集合。
  • 连接点(Joinpoint):程序运行时可以切入切面执行通知的点。例如执行方法前、执行方法后、程序异常时等都可以作为连接点。
  • 切点(Pointcut):提供规则,匹配符合要求的连接点。满足切点规则的连接点才可以切入切面执行逻辑。
  • 通知(Advice):包括切入切面后执行的功能和执行时机。可以使用注解的方式来确定执行时机。

AOP的优点

  • 提高代码复用性和可维护性: AOP可以像函数封装那样,不必重复写相同的代码;
  • 提高开发的效率:AOP可以把不同的业务功能分块,例如日志、安全等模块分成不同的切面,在每个切面处理特定的功能;

Spring AOP

Spring AOP虽然属于Spring全家桶的产品,但是在创建项目时并不能找到相应的依赖:

 因此需要自行添加(注意版本号):

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.7.13</version>
</dependency>

使用Spring AOP

定义切面和切点

使用@Aspect注解表示当前类为一个切面,使用@Pointcut注解定义切点(即设定规则):

@Component
@Aspect
public class ExperimentalAspect {

    @Pointcut("execution(* com.example.demo.controller.ExperimentalController.*(..))")
    public void pointcut() {}// 空方法,只起到标识的作用

}

@Pointcut注解的参数是一个切点表达式,用于匹配连接点。使用AspectJ语法。切点表达式由切点函数组成,如:execution()、within()、target()、@annotation()等,其中最常用的时execution()函数,用来匹配方法连接点,语法为:

execution(<访问权限修饰符(可省略)><返回类型><包.类.方法(参数)><异常(可省略)>)

访问权限修饰符省略不写表示匹配所有权限,除参数部分外任意一个部分都可以使用通配符'*'来匹配任意字符,例如返回类型使用'*'表示任意返回值都满足规则。

参数中写具体的类型例如(int)表示参数为一个整型,使用'..'表示任意参数都满足。

上文的@Pointcut参数的含义就是匹配任意权限且在ExperimentalController包中的任意方法(返回值任意,参数任意)

其他切点函数用法可以参考Spring官网:Declaring a Pointcut :: Spring Framework

定义通知

Spring AOP通知注解包括:

  • @Before——方法执行前执行通知
  • @After——方法执行后执行通知(方法执行失败也会执行通知)
  • @AfterReturning——方法返回后(成功执行方法)执行通知
  • @AfterThrowing——抛出异常时执行通知
  • @Around——方法执行前后执行通知
// 执行前置通知
@Before("pointcut()")
public void runBefore() {
    System.out.println("执行了前置通知");
}
// 执行后置通知
@After("pointcut()")
public void runAfter() {
    System.out.println("执行了后置通知");
}

类似的@AfterReturning和@AfterThrowing写法一样,注解参数设置为上文设置的声明切点规则的函数名即可。

@Around注解除了遵守这个规则外,还需要设置返回值为Object类,参数设置为ProceedingJoinPoint类:

@Around("pointcut()")
public Object runAround(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("开始执行环绕方法");
    Object obj = null;
    // 执行拦截方法
    obj = joinPoint.proceed();
    System.out.println("结束执行环绕方法");
    return obj;
}

动态代理 

Spring AOP是基于动态代理技术实现的。调用者首先要通过代理才能到达目标对象,就比如说在内陆访问chatgpt,就要先通过代理,代理就会把这些ip的请求拦截下来,因此在内陆无法访问chatgpt等。动态代理包含两种方式:JDK动态代理和CGLib动态代理。

  • JDK动态代理:被代理类需要实现接口,然后通过反射机制,生成代理对象。
  • CGLib动态代理:被代理类可以不实现接口,通过继承被代理类的方式,生成代理对象。

如果目标类实现了至少一个接口,采用 JDK 动态代理来为目标对象创建代理对象。

JDK 自带的 java.lang.reflect 包下的 Proxy 类提供了创建接口代理实例的功能。当调用代理对象的方法时,实际会调用 InvocationHandler 的 invoke 方法,该方法可以在调用前后添加额外的操作,即所谓的“切面”。

对于没有实现接口的类,Spring AOP 使用 CGLIB 创建代理对象。

CGLIB 是一个第三方库,能够在运行期动态生成字节码,从而创建目标类的子类。CGLIB 子类重写了目标类的方法,在方法调用前后插入增强处理,同样实现切面织入。

可以在配置文件中设置属性为true强制使用CGLib代理:

// 强制使用CGLib代理
spring.aop.proxy-target-class=true

织入

织入,把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中。简单来说就是动态代理的生成时机。织入可以在编译期、类加载期和运行期。Spring AOP默认是在运行期织入切面的。

在运行时,每当Spring容器创建一个bean时,如果该bean被声明为一个切面或需要被切面增强,Spring会动态地创建一个代理对象。这个代理对象的方法在执行时会先执行切面逻辑,然后再调用原目标对象的方法,从而实现了横切关注点的织入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值