前言
AOP的核心思想是面向切面编程。AOP规范定义了多种概念,常用的aop框架有spring aop和AspectJ,两者功能和性能差异较大,现在默认的AOP框架是AspectJ,下面逐渐归纳其相关概念、功能及实现原理。
1. 概念
1. 切面: 通常为一个具体的实体类,用@Aspect标注,代表由一个或多个切点组成的切面,因为可以由多个切点,所以一个切面类它可以是多维度、多条件的。
2. 切点:即@PointCut,它是由一个或者多个条件形成的切入点
3. 连接点: 连接点,ProceedingJoinPoint, 即满足切点条件后,每个请求具体在某个类、某个方法上的连接点,因此连接点是请求级的,每个请求的连接点可能都不一样。
2. 切面创建
切面通常代表一个具体的实体类,用@Aspect标注,除此之外,在spring中,还需要将该类用@Component标注为一个实体。
3. 切点定义
切点是由一个或者多个条件组成的,这里面有两个元素:条件表达式和条件逻辑连接
条件的逻辑连接及常用的与&& 、 或|| 、非!,可以通过括号()等来确定。不同类型的条件表达式可以通过逻辑符号连接。
而条件表达式可以聚焦不同的维度来编写, 下面以全路径为com.example.demo.controller.TestController的类来举例:
1. 目标对象(target)
@Pointcut("target(com.example.demo.controller.TestController)")
public void pointCut(){}
在target表达式中,需要用类的全路径。针对这个类中所有接口。
2. IOC对象(bean)
@Pointcut("bean(testController)")
public void pointCut(){}
在bean的表达式中,需要用IOC容器名称。针对这个bean对象中的所有接口。
3. 方法级(execution)
@Pointcut("execution(* com.example.demo.controller.TestController(*.*))")
public void pointCut(){}
4. 方法级(@annotation)
自定义一个注解com.example.demo.controller.Ts,并在测试类的方法上添加该注解。
@Pointcut("@annotation(com.example.demo.controller.Ts)")
public void pointCut(){}
5. 其他
除了上述几种条件表达式外,还有其他表达式如args、within等等。
3. 通知类型
通知类型有5种,@Before前置通知、@Around环绕通知、@AfterReturn响应后通知、@AfterThrowing异常后通知、@After后置通知。
当有多个通知类型时的优先级顺序。下面还是以TestController的这个切点来举例,并且添加所有的通知类型,其中环绕通知执行joinPoint.proceed();并前后打点。
结果如下:
由此可知,当有@Around环绕通知时,先进入@Around环绕通知,最后退出@Around环绕通知。
Around前置 > Before > 业务接口 >AfterReturn(AfterThrowing) > After > Around后置
Before 前置通知
前置通知通常用于做进入连接点前的验证或者拦截,强调在业务之前做增强处理。
AfterReturn 正常响应通知
该通知用于在业务接口正常响应以后,做增强处理。
AfterThrowing 异常响应通知
该通知用于在业务接口异常响应后,做增强处理。
后置通知
该通知在AfterReturn 或 AfterThrowing后,不论是否正常还是异常响应,都会经过。可以做后置强制处理。
环绕通知
环绕通知需要引入连接点,ProceedingJoinPoint joinPoint,具体的业务在joinPoint.proceed();过程中执行。前置和后置通知可以在该代码块前后处理。
4. 原理实现
代理分为静态代理和动态代理,动态代理又分为JDK动态代理和Cglib动态代理。
其中,JDK动态代理是java原生api,需要被代理类有接口实现,所以JDK动态代理又称为接口代理。
Cglib动态代理不需要实现接口,又称为子类代理,不需要额外的接口实现。