Spring一开始最强大的就是IOC/AOP这两个核心功能。今天就一起来学习一下SpringAOP的常用注解和执行顺序。SpringAop的常用注解首先我们回顾一下SpringAop中一些常用的注解:@Beforepre-advice:在目标方法之前执行@Afterpost-advice:在目标方法之后执行(一直执行)@AfterReturningnotifyafterreturn:执行执行方法结束前(异常不执行)@AfterThrowing异常通知:异常发生后执行@Around环绕通知:围绕目标方法执行FAQ还是SpringBoot2对aop执行顺序的影响?2.说说你在AOP中遇到的坑?示例代码让我们快速构建一个springaop演示程序来讨论springaop的一些细节。配置文件为了方便我直接使用spring-boot快速构建项目,可以使用idea的spring-boot项目快速创建功能,或者去start.spring.io快速创建spring-boot应用。由于自己经常在网上手动贴一些依赖,出现依赖冲突服务启动失败等问题。plugins{id'org.springframework.boot'version'2.6.3'id'io.spring.dependency-management'version'1.0.11.RELEASE'id'java'}group'io.zhengsh'version'1.0-SNAPSHOT'repositories{mavenCentral()maven{url'https://repo.spring.io/milestone'}maven{url'https://repo.spring.io/snapshot'}}dependencies{#其实没有需要添加对于web配置,请忽略implementation'org.springframework.boot:spring-boot-starter-web'implementation'org.springframework.boot:spring-boot-starter-actuator'implementation'org.springframework.boot:spring-boot-starter-aop'testImplementation'org.springframework.boot:spring-boot-starter-test'}tasks.named('test'){useJUnitPlatform()}接口类首先我们需要定义一个接口。这里我们可以回顾一下JDK默认代理实现的选择:如果目标对象实现了接口,则默认使用JDK动态代理;如果目标对象没有实现接口,则使用动态代理;如果目标对象实现了接口,强制使用cglib,使用cglib代理的逻辑在DefaultAopProxyFactory中。如果你有兴趣,你可以看看。publicinterfaceCalcService{publicintdiv(intx,inty);}实现类这里简单的做一个除法运算,可以模拟正常也可以很容易的模拟错误。@ServicepublicclassCalcServiceImplimplementsCalcService{@Overridepublicintdiv(intx,inty){intresult=x/y;System.out.println("====>调用了CalcServiceImpl,我们的计算结果为:"+result);返回结果;}}aopinterceptor声明一个拦截器。我们需要为当前对象添加@Aspect和@Component。笔者之前也只踩过这样的坑,只加了一个。其实一开始我不是很理解这个,但是看了Aspect注解的定义后,才发现根本就没有Bean的定义。于是我们乖乖加了两个注解。另外,如果测试时需要开启Aop支持,在配置类中添加@EnableAspectJAutoProxy注解即可。其实Aop使用三个步骤:DefineAspectDefineAspectDefinePointcutDefineourentrypoint定义具体的通知,比如:@After,@Before等。@Aspect@ComponentpublicclassMyAspect{@Pointcut("执行(*io.zhengsh.spring.service.impl..*.*(..))")publicvoiddivPointCut(){}@Before("divPointCut()")publicvoidbeforeNotify(){System.out.println("----===>>@Before我是预通知");}@After("divPointCut")publicvoidafterNotify(){System.out.println("----===>>@After我发布通知");}@AfterReturning("divPointCut")publicvoidafterReturningNotify(){System.out.println("----===>>@AfterReturning我是预通知");}@AfterThrowing("divPointCut")publicvoidafterThrowingNotify(){System.out.println("----===>>@AfterThrowing我是异常通知");}@Around("divPointCut")publicObjectaround(ProceedingJoinPointproceedingJoinPoint)throwsThrowable{ObjectretVal;System.out.println("----===>>AAA前@Around环绕通知");retVal=proceedingJoinPoint.proceed();System.out.println("----===>>@AroundBBB");returnretVal;}}测试类其实我的测试类,虽然我用了@Test注解,但是我的类更像是一个main方法:如下图:执行结论结果记录:spring4.x,spring-boot1.5.9现在不能依赖,所以没法测试,直接说结论:在Spring4中,surroundnotification是在result的最里面执行的记录:springversion5.3.15springbootVersion2.6.3多切面情况下多切面情况下,可以通过@Order指定顺序,数字越小,优先级越高,如下图所示:代理失败场景下面的场景会导致aop代理失败,因为当我们执行a方法时,本质上是执行AServer#a的方法拦截器(MethodInterceptor)链,当我们直接执行a中的b()方法,本质其实就相当于this.b(),此时执行的a方法被调用到a的原始对象,相当于这个调用,会导致b()方法的代理失败。这个问题也是我们开发人员在开发过程中遇到最多的问题。@ServicepublicclassAService{publicvoida(){System.out.println("...a");b();}publicvoidb(){System.out.println("...b");}}
