**
spring Aop通知执行顺序问题
今天在测试spring Aop日志通知发现SpringBoot 2.3.0版本的通知和2.3.2版本的通知执行顺序不一样简单来说一下
springboot2.3.0版本
这个图是我了解的执行的顺序,也是2.3.0版本的执行过程
这个是导入的包的版本
@Component
@Aspect
public class SysTimeAspect {
@Pointcut("bean(sysUserServiceImpl)")
public void doTime(){}
@Before("doTime()")
public void doBefore(JoinPoint jp){
System.out.println("time doBefore()");
}
@After("doTime()")
public void doAfter(){
System.out.println("time doAfter()");
}
/**核心业务正常结束时执行* 说明:假如有after,先执行after,再执行returning*/
@AfterReturning("doTime()")
public void doAfterReturning(){
System.out.println("time doAfterReturning");
}
/**核心业务出现异常时执行说明:假如有after,先执行after,再执行Throwing*/
@AfterThrowing("doTime()")
public void doAfterThrowing(){
System.out.println("time doAfterThrowing");
}
@Around("doTime()")
public Object doAround(ProceedingJoinPoint jp)
throws Throwable{
System.out.println("doAround.before");
try{
Object obj=jp.proceed();
System.out.println("doAround.after");
return obj;
}catch(Throwable e){
System.out.println(e.getMessage());
throw e;
}
}
}
这里写了一个测试运行结果如下
这里的执行顺序是先执行@Around注解的方法,在连接点方法jp.proceed()执行之前输出doAround.before然后在执行@Before注解的方法,输出time doBefore(),然后执行连接点方法jp.proceed(),方法成功运行后继续执行@Around注解的方法里还未走完的代码,输出doAround.after,再执行@After注解的方法,输出time doAfter(),最后在执行@AfterReturning的方法,输出time doAfterReturning。如果要是连接点方法**jp.proceed()**出现异常则执行@AfterThrowing注解的方法。
springboot2.3.2版本
可是当我改成2.3.2版本时
输出结果
在springboot2.3.2版本里是在连接点方法**jp.proceed()**执行成功后,先执行@AfterReturning下的方法,在执行@After下的方法,最后在输出的doAround.after
最后顺便说一下切入点表达式的增强
Spring中通过切入点表达式定义具体切入点,其常用AOP切入点表达式定义及说明:
表-1 Spring AOP 中切入点表达式说明
指示符 | 作用 |
---|---|
bean | 用于匹配指定bean对象的所有方法 |
within | 用于匹配指定包下所有类内的所有方法 |
execution | 用于按指定语法规则匹配到具体方法 |
@annotation | 用于匹配指定注解修饰的方法 |
bean表达式
bean表达式一般应用于类级别,实现粗粒度的切入点定义,案例分析:
- bean(“userServiceImpl”)指定一个userServiceImpl类中所有方法。
- bean("*ServiceImpl")指定所有后缀为ServiceImpl的类中所有方法。
- 说明:bean表达式内部的对象是由spring容器管理的一个bean对象,表达式内部的名字应该是spring容器中某个bean的name。
within表达式
within表达式应用于类级别,实现粗粒度的切入点表达式定义,案例分析:
- within(“aop.service.UserServiceImpl”)指定当前包中这个类内部的所有方法。
- within(“aop.service.*”) 指定当前目录下的所有类的所有方法。
- within(“aop.service…*”) 指定当前目录以及子目录中类的所有方法。
- within表达式应用场景分析:
1)对所有业务bean都要进行功能增强,但是bean名字又没有规则。
2)按业务模块(不同包下的业务)对bean对象进行业务功能增强。
execution表达式
execution表达式应用于方法级别,实现细粒度的切入点表达式定义,案例分析:
- 语法:execution(返回值类型 包名.类名.方法名(参数列表))。
- execution(void aop.service.UserServiceImpl.addUser())匹配addUser方法。
- execution(void aop.service.PersonServiceImpl.addUser(String))
方法参数必须为String的addUser方法。 - execution(* aop.service….(…)) 万能配置。
@annotation表达式
@annotaion表达式应用于方法级别,实现细粒度的切入点表达式定义,案例分析
- @annotation(anno.RequiredLog) 匹配有此注解描述的方法。
- @annotation(anno.RequiredCache) 匹配有此注解描述的方法。
其中:RequiredLog为我们自己定义的注解,当我们使用@RequiredLog注解修饰业务层方法时,系统底层会在执行此方法时进行日扩展操作。
**
总结一下aop:在不改变原有代码的条件下,对功能进行扩展.
专业术语:
切面(aspect): 横切面对象,一般为一个具体类对象(可以借助@Aspect声明)。
通知(Advice):在切面的某个特定连接点上执行的动作(扩展功能),例如around,before,after等。
连接点(joinpoint):程序执行过程中某个特定的点,一般指向被拦截到的目标方法。
切入点(pointcut):对多个连接点(Joinpoint)一种定义,一般可以理解为多个连接点的集合。