一、AOP是什么
二、AOP应用原理初步分析
有两种方式
1、借助JDK官方API为目标对象类型创建其兄弟类型对象,但是目标对象类型需要实现相应接口
2、借助CGLIB库为目标对象类型创建其子类类型对象,但是目标对象类型不能使用final修饰.
三、AOP相关术语
1、切面对象(Aspect)
封装了扩展业务逻辑的对象,在spring中可以使用@AspectJ描述
2、切入点(Pointcut)
定义了切入扩展业务逻辑的一些方法的集合(哪些方法运行时切入扩展业务),一般会通过表达式进行相关定义,一个切面中可以定义多个切入点的定义
3、连接点(JoinPoint)
切入点方法集合中封装了某个正在执行的目标方法信息的对象,可以通过此对象获取具体的目标方法信息,甚至去调用目标方法
4、通知(Advice)
切面(Aspect)内部封装扩展业务逻辑的具体方法对象,一个切面中可以有多个通知(例如@Around)
可以简单的将机场的一个安检口理解为连接点,多个安检口为切入点,安全检查过程看成是通知
四、Spring中关于Aop的注释
1、@Aspect
该注解描述的类型为切面对象类型,此切面中可以定义多个切入点和通知方法
2、@Pointcut
该注解用于定义切入点
3、@Around
该注解描述的方法,可以在切入点执行之前和之后进行业务拓展,在当前业务中
五、Spring切面对象定义
在springboot工程中,切面对象需要@Aspect
1 package XXX; 2 3 @Slf4j 4 @Aspect 5 @Component 6 public class SysLogAspect { 7 /** 8 * bean("spring容器中bean的名字")这个表达式为切入点表达式定义的一种语法, 9 * 它描述的是某个bean或多个bean中所有方法的集合为切入点,这个形式的切入点 10 * 表达式的缺陷是不能精确到具体方法的. 11 */ 12 @Pointcut("bean(sysUserServiceImpl)") 13 public void doLog(){}//此方法只负责承载切入点的定义 14 15 /** 16 * 此方法为日志通知方法 17 * @param joinPoint 连接点对象,此对象封装了要执行的切入点方法信息.可以通过连接点对象调用目标方法,这里的ProceedingJoinPoint类型只能应用于@Around描述的方法参数中 18 * @return 目标方法的执行结果 19 * @throws Throwable 20 */ 21 @Around("doLog()") 22 public Object doAround(ProceedingJoinPoint jp)throws Throwable{ 23 log.info("Start:{}",System.currentTimeMillis()); 24 try { 25 Object result = jp.proceed();//执行目标方法(切点方法中的某个方法) 26 log.info("After:{}",System.currentTimeMillis()); 27 return result;//目标业务方法的执行结果 28 }catch(Throwable e){ 29 e.printStackTrace(); 30 log.error("Exception:{}",System.currentTimeMillis()); 31 throw e; 32 } 33 } 34 }
六、Spring切面工作原理分析
切面内部,切入点对应的目标业务方法执行时,底层会通过代理对象访问切面中的通知方法,进而通过通知方法为目标业务做功能增强.
JDK动态代理对象应用分析:
其中:springboot工程默认的AOP代理为CGLIB代理,假如希望是JDK代理,则需要在springboot的配置文件中进行如下配置:
spring: aop: proxy-target-class: false
CGLIB动态代理对象应用分析:
七、Aop通知类型
Spring框架AOP模块定义通知类型,有如下几种:
1、@Around (所有通知中优先级最高的通知,可以在目标方法执行之前,之后灵活进行业务拓展.)
2、@Before (目标方法执行之前调用)
3、@AfterReturning (目标方法正常结束时执行)
4、@AfterThrowing (目标方法异常结束时执行)
5、@After (目标方法结束时执行,正常结束和异常结束它都会执行)
八、Aop切入点表达式
在Spring工程中对于切入点表达式,可以分成两大类型:
-
粗粒度切入点表达式定义(不能精确到具体方法),例如bean,within表达式
-
细粒度切入点表达式定义(可以精确到具体方法),例如execution,@annotation表达式
粗粒切入点表达式
1、bean(“bean的名字”) 表达式案例分析
1 1) bean(XxxServiceImpl),XxxServiceImpl类中所有方法集合为切入点 2 2) bean(*ServiceImpl),以ServiceImpl结尾的所有方法集合为切入点
2、within ("包名.类型") 表达式案例分析
1 1) within(XxxServiceImpl),XxxServiceImpl类中所有方法集合为切入点 2 2) within(Xxx.*),Xxx.service包下所有类中的方法集合为切入点 3 3) within(Xxx.service..*),Xxx.service包以及子包中所有类中方法的集合为切入点
细粒度切入点表达式
3、execution("返回值 类全名.方法名(参数列表)") 表达式案例分析
1 1) execution(int XxxService.validById(Integer,Integer)) 2 2) execution(* Xxx.service..*.*(..))
4、@annotation(“注解的类全名”)表达式案例分析
1 1) @annotation(Xxx),由RequiredCache注解描述的方法为缓存切入点方法 2 2) @annotation(Xxx),由RequiredLog注解描述的方法为日志切入点方法
九、Aop切面优先级设置
当有多个切面时,并且对应着相同的切入点,假如切面中的通知方法的执行需要有严格顺序要求,此时我们需要设置切面的优先级。这个优先级可以通过@Order(数字优先级)注解进行描述,数字越小优先级越高。
设置日志切面优先级:
1 @Order(2) 2 @Aspect 3 @Component 4 public class XXX{}
设置缓存切面优先级:
1 @Order(1) 2 @Aspect 3 @Component 4 public class XXX{}
假如默认没有指定优先级,默认是最低的优先级。相同切入点下的多个切面会形成一个切面链。