spring框架的AOP简单入门

AOP指的是Aspect Oriented Programming,即面向切面编程。其实就是面向特定方法(一个或多个)编程。要解释其含义,最好从其应用场景入手,比如要对所有方法的执行进行日志记录,或者某些功能运行较慢因此需要对业务方法进行计时从而定位到耗时较长的方法来进行优化。这种需求通常是对每个方法都需要进行的,因此可以将需求抽象成一个单独的模板方法,从而不需要修改原来的方法就可以添加我们希望的功能:

  1. 先获取方法开始运行的时间。
  2. 运行原始的方法(包含业务逻辑)。
  3. 获取方法结束的时间,计算执行时长。

java的动态代理技术就是aop的最主流实现。SpringAOP是spring框架的一种高级技术,就是通过底层的动态代理机制,对特定的方法进行编程。

使用springAOP需要引入依赖spring-boot-starter-aop。要编写aop程序,还需要定义一个类(aop类),该类需要交给IoC容器管理,因此需要加上两个注解:@Component@Aspect。要运行原始方法,本方法的形参列表中需要声明一个ProceedingJoinPoint对象参数,注意该参数封装了原始方法(比如调用它提供的方法可以获取原始方法的签名等),在调用原始方法运行时需要调用该参数的proceed()方法。注意该方法可能会抛出异常,返回值为Object类型,该返回值最后需要从aop方法中返回出去。

aop编程是针对特定的方法的,这意味着我们需要指定该方法是针对哪些特定方法的。springAOP通过指定@Around注解来指定该切面方法针对的是哪些方法,该注解的参数通过指定包、类和方法名来指定对应的方法(通过切入点表达式指定)。

AOP的应用场景包括:

  1. 记录操作日志。
  2. 权限控制。
  3. 事务管理。spring boot的事务管理就是通过aop实现的。

优势包括:

  1. 代码无侵入。不需要修改原始的业务方法,让业务方法仅负责业务逻辑处理。
  2. 减少重复代码。
  3. 提高开发效率。
  4. 维护方便。

下面记录一下AOP的核心概念。

  1. JoinPoint连接点,指的是可以被aop控制的方法(这里基本上指的就是业务逻辑方法),它包含方法执行时的相关信息。
  2. Advice通知,指的是重复的逻辑(共享功能),最终在程序中体现为一个方法。(aspect类中的方法)
  3. PointCut切入点,匹配连接点的条件,advice方法仅会在切入点方法执行时被调用。
  4. Aspect切面,由切入点表达式和advice组成,描述的是这个aop程序针对哪些特定方法、在具体什么时候、进行什么操作。
  5. 切面类,指的是用注解标注的类。
  6. 目标对象,指advice所应用的对象。

之前提到springAOP底层基于java的动态代理技术实现的,就是说程序运行时会自动基于目标对象生成对应的代理对象,代理对象中会对目标对象中原始的方法进行增强(添加切面方法中的操作逻辑)。程序实际运行时依赖注入的并不是原始的目标对象,而是对应的代理对象。

通知类型和执行顺序

springAOP提供了几种不同的通知类型:

  1. @Around,此前使用的就是它,标注的通知方法在目标方法的前后都被执行。
  2. @Before
  3. @After,无论目标方法是否有异常都会执行。
  4. @AfterReturning,返回后执行,有异常就不会执行。
  5. @AfterThrowing,异常后执行,即发生异常后执行(正常情况不会执行)。

注意一个aop类中的aop方法的切入点表达式可能存在很多重复,此时可以抽取一个单独的切入点方法(一个空方法,如果你想在其他类中使用这个表达式,就需要将它设置为public),通过@PointCut一次性设置切入点表达式,然后在其他方法中引用这个表达式。这个注解的作用就是将公共的切入点表达式抽取处理,供需要时引用。

还有一个注意点在于多个通知方法的执行顺序(主要是目标方法后的方法):目标方法执行后,先是返回后/异常后执行,然后是after执行,最后是around的后半部分执行。注意如果有多个相同注解的切面方法,其在框架中的执行顺序默认是不确定的。

还需要指出的是如果有多个切面的切入点都匹配到了同一个目标方法,它们会通过一定顺序被执行。决定执行顺序的原则是按照切面类的类名字母顺序:

  • 目标方法前:字典序小的先执行。
  • 目标方法后:字典序小的反而后执行。

如果要人为控制执行顺序,可以使用@Order()注解加上数字对切面类进行注解,执行顺序类似上面的规则:

  • 目标方法前:数字小的先执行。
  • 目标方法后:数字小的后执行。

切入点表达式

@execution注解根据方法的各种信息(返回值、包名、类名、方法名、参数等)来匹配目标方法,可以使用通配符如*..。也可以通过使用逻辑运算符来组合比较复杂的切入点表达式。

除此之外还可以使用@annotation来匹配标识有特定注解的目标方法(参数为注解的全类名)。有时方法名没有什么规律,通过方法名相关信息不好匹配,可以自定义注解(起标识作用)并结合@annotation注解来完成目标方法的匹配。

这两个注解的具体使用应该取决于实际需求,并没有说哪个更好。

连接点JoinPoint

spring提供的JoinPoint对连接点的概念进行了抽象,使用它可以获得匹配到的目标方运行时的相关信息(类名、方法名、参数等)。有两点需要注意:

  1. 对于环绕通知@Around,只能使用ProceedingJoinPoint,该类让程序员可以手动调用目标方法执行。
  2. 而对于其他四种通知,只能使用JoinPoint,该类是ProceedingJoinPoint的父类。

还有一点需要注意的是JoinPointorg.aspectj.lang包中的类,idea自动导入时需要格外注意不要导入其他包中的同名类,否则项目在启动时会报错无法正常启动,且这个错误难以排查。

posted @ 2025-02-14 17:06  随机生成一个id  阅读(47)  评论(0)    收藏  举报