AOP介绍
AOP(面向切面编程,Aspect Oriented Programming)是对面向对象编程的补充。通常会把一些公共操作放到切面中,避免这些公共逻辑散落到各个对象中。平时开发中切面思想也很常用,比如过滤器、拦截器、ControlllerAdvice。
原理
SpringAOP是基于代理实现的,代理对象中会执行公共逻辑。借用一张Spring官网的示意图:
我们调用代理对象的foo()函数,代理对象调用原始对象的foo函数。只是在调用原始对象的foo函数之前(或之后),代理对象会执行公共逻辑。
在Spring中,我们不需要写代理类,只需要写公共逻辑,并告诉Spring,这些公共逻辑加在哪些方法的前(后)即可。
概念
一般,某个对象的一个方法被代理了,我们说这个方法被增强了。
连接点(JointPoint):被增强的方法。
切点(PointCut):连接点的定义。可以理解为批量指定需要被增强的方法。
通知/增强(Advice):增强的逻辑。
使用SpringAOP
引入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
SpringAOP要点
- 在本文的介绍部分,我们看到切面可以有多个,这就涉及到执行的先后顺序问题。切面的执行顺序可以用@Order指定,数值越小,越早执行。
- Advice是增强逻辑,这个增强逻辑在原有逻辑之前还是之后执行呢?都可以。根据执行时机的不同,把Advice分为
@Before:在原有逻辑之前执行的逻辑。
@AfterReturning:在原有逻辑正常返回后执行的逻辑。
@AfterThrowing:在原有逻辑抛出异常后执行的逻辑。
@After:在@AfterReturning或@AfterThrowing之后执行的逻辑,类似于try…catch…finally中的finally,不关是否抛出异常,都会执行。
@Around:在原有逻辑前后执行的逻辑。在这个Advice中一般会调用org.aspectj.lang.ProceedingJoinPoint#proceed()函数,它的作用是调用原有逻辑。以它为分界线,之前的逻辑会在原有逻辑之前执行,之后的逻辑会在原有逻辑之后执行。
注意这里@AfterThrowing是针对原有逻辑而言的,只有当原有逻辑中抛出了异常,才会触发@AfterThrowing中的逻辑,如果@Before或者其他类型Advice中发生异常,则增强链直接终止。 - 在一个切面内部,各种类型的Advice执行顺序是这样的:(注意这里使用SpringBoot2.7.0,低版本的SpringBoot中执行顺序可能不一样,参考
你真的确定SpringAOP的执⾏顺序吗和Spring Aop执行顺序 深入理解Spring Aop的执行顺序
- 多个切面,如果执行中没有异常抛出,执行顺序是这样的(同样,这里说的是SpringBoot2.7.0):
这里可以看出,proceed()不一定会触发原有逻辑的执行,如果后续还有切面,则会触发下一个切面增强逻辑;如果后续没有切面了,才会触发原有逻辑的执行。这也可以解释为什么在多个切面(即多个proceed)的情况下,原有逻辑不会被多次执行。
@Around类似于Servlet过滤器的doFilter(ServletRequest , ServletResponse , FilterChain)方法,proceed类似FilterChain的doFilter(ServletRequest , ServletResponse )方法。在FilterChain调用doFilter方法时,如果后续还有过滤器,就会执行下一个过滤器的逻辑,如果后续没有过滤器,就会执行Servlet的逻辑。
多个切面,如果执行中有异常抛出,执行顺序是这样的:
从上图可以看出,当原始逻辑抛出异常后,只有一个切面执行了@AfterThrowing逻辑,其他的切面执行的是@AfterReturning逻辑。 - @Around中可以修改原始逻辑的返回值。但是类型要能兼容。在Aspect2的@AfterReturning中拿到的是Aspect3在@Around中返回的结果。