SpringBoot之AOP的使用spring核心思想分三大类:控制反转(IOC),依赖注入(DI)和面向切面(AOP)
1.什么是面向切面编程
AOP简介
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
为什么使用AOP编程范式?
分离功能性需求和非功能性需求
集中处理某一关注点
侵入性少,增强代码可读性及可维护性
AOP应用场景
权限控制、缓存控制、事务控制、分布式追踪、异常处理等
总之,在程序运行时,动态地将代码切入到类的指定方法或位置上的思想,就是面向切面编程。
2.AOP常用术语Spring的AOP中有几个重要概念搞清楚就行
执行点(Executepoint) - 类初始化,方法调用。
连接点(Joinpoint) - 执行点+方位的组合,可确定Joinpoint,比如类开始初始化前,类初始化后,方法调用前,方法调用后。
切点(Pointcut) - 在众多执行点中,定位感兴趣的执行点。Executepoint相当于数据库表中的记录,而Pointcut相当于查询条件。
增强(Advice) - 织入到目标类连接点上的一段程序代码。除了一段程序代码外,还拥有执行点的方位信息。
目标对象(Target) - 增强逻辑的织入目标类
引介(Introduction) - 一种特殊的增强(advice),它为类添加一些额外的属性和方法,动态为业务类添加其他接口的实现逻辑,让业务类成为这个接口的实现类。
代理(Proxy) - 一个类被AOP织入后,产生一个结果类,它便是融合了原类和增强逻辑的代理类。
切面(Aspect) - 切面由切点(Pointcut)和增强(Advice/Introduction)组成,既包括横切逻辑定义,也包括连接点定义。
AOP工作重点:
如何通过切点(Pointcut)和增强(Advice)定位到连接点(Jointpoint)上;
如何在增强(Advice)中编写切面的代码。
3.Advice-五种增强方式例如在执行某个特定方法的时候,我们可以选择不同的增强方式(如前置通知/增强,在方法运行前执行),达到我们织入后的不同效果。
前置通知:在我们执行目标方法之前运行(@Before)
1 2 3 4 5 6 @Pointcut("within(com.example.demo.Service.\*)") public void matchType () {}@Before("matchType()") public void before (JoinPoint joinPoint) { System.out.println("------【前置通知】------" + joinPoint); }
JAVA
后置通知:在我们目标方法运行结束之后 ,不管有没有异常(@After)
1 2 3 4 @After(value="execution(\* com.example.aspectJ.demo1.ProductDao.findAll(..))") public void after () { System.out.println("最终通知==================" ); }
JAVA
返回通知:在我们的目标方法正常返回值后运行(@AfterReturning)
1 2 3 4 @AfterReturning(value="execution(\* com.example.aspectJ.demo1.ProductDao.update(..))" ,returning = "result") public void afterReturning (Object result) { System.out.println("后置通知=========" +result); }
JAVA
异常通知:在我们的目标方法出现异常后运行(@AfterThrowing)
1 2 3 4 5 @AfterThrowing(value = "execution(\* com.example.aspectJ.demo1.ProductDao.findOne(..))",throwing = "e") public void afterThrowing (Throwable e) { System.out.println("抛出异常通知" +e.getMessage()); }
JAVA
环绕通知:动态代理, 需要手动执行joinPoint.procced()(其实就是执行我们的目标方法执行之前相当于前置通知, 执行之后就相当于我们后置通知(@Around)
1 2 3 4 5 6 7 @Around(value = "execution(\* com.example.aspectJ.demo1.ProductDao.delete(..))") public Object around (ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("环绕前通知" );Object obj = joinPoint.proceed(); System.out.println("环绕后通知" );return obj; }
JAVA
4.SpringAOP使用详解切面表达式
excution表达式
execution(
修饰符pattern
返回值pattern
描述包名
方法名(参数)
方法抛出异常pattern
)
1 2 3 4 5 6 @Pointcut("execution(public \* com.example.controller.\*Controller.\*(..))") public void match () {}@Before("match()") public void before () { }
JAVA
within表达式
1 2 3 4 5 6 @Pointcut("within(com.example.service.StudentService)") public void matchType () {}@Pointcut("within(com.example..\*)") public void matchPackage () {}
JAVA
对象匹配
1 2 3 4 5 6 7 8 9 10 /\*public class serviceImpl implements service \*/@Pointcut("this(com.example.serviceImpl)") public void thisDemo () {}@Pointcut("target(com.example.service)") public void targetDemo () {}@Pointcut("bean(\*Service)") public void beanDemo () {}
JAVA
参数匹配
1 2 3 4 5 6 7 8 9 10 11 12 @Pointcut("execution(\* \*..find\*(Long))") public void argsDemo1 () {}@Pointcut("args(Long)") public void argsDemo2 () {}@Pointcut("execution(\* \*..find\*(Long,..))") public void argsDemo3 () {}@Pointcut("args(Long,..)") public void argsDemo4 () {}
JAVA
注解匹配(自定义注解)
1 2 3 4 5 6 7 8 9 10 11 12 @Pointcut("@annotation(com.example.security.AdminOnly)") public void annoDemo () {}@Pointcut("@within(com.example.annotation.Test1)") public void annoWithinDemo () {}@Pointcut("@target(com.example.repository.Test2)") public void annoTargetDemo () {}@Pointcut("@args(org.example.repository.Test3)") public void annoArgsDemo () {}
JAVA
两个aop切面类都工作了,顺序呢就是下面的:
spring aop就是一个同心圆,要执行的方法为圆心,最外层的order最小。从最外层按照AOP1、AOP2的顺序依次执行doAround方法,doBefore方法。然后执行method方法,最后按照AOP2、AOP1的顺序依次执行doAfter、doAfterReturn方法。也就是说对多个AOP来说,先before的,一定后after。
对于上面的例子就是,先外层的就是对所有controller的切面,内层就是自定义注解的。 那不同的切面,顺序怎么决定呢,尤其是同格式的切面处理,譬如两个execution的情况,那spring就是随机决定哪个在外哪个在内了。
所以大部分情况下,我们需要指定顺序,最简单的方式就是在Aspect切面类上加上@Order(1)注解即可,order越小最先执行,也就是位于最外层。像一些全局处理的就可以把order设小一点,具体到某个细节的就设大一点。
5.参考文档SpringAOP-什么是面向切面编程?
SpringBoot系列 - 使用AOP
6.GitHub源码springboot-aop