Spring AOP vs AspectJ
本文对于学习没什么参考性,写的也比较杂乱,是在学习过程中的一个笔记,有地方可能还串不起来,个人在学习时的一些关键点记录。
AOP keywords
- Aspect, 横切面,对象
- Jointpoint, 连接点,在Spring里是方法,AspectJ还可以是其他比如构造器等
- Pointcut, 切入点,连接点拦截的定义
- Advice, 通知,拦截到拦截点之后要做的动作(Before, After, Around, AfterReturning, AfterThrowing)
- Weaving, 织入(编译期,类加载期(AspectJ5 LTW),运行期(Spring AOP基于此,动态创建代理))
Spring中对AOP的支持
- 基于代理的Spring AOP及其变种(ProxyFactoryBean等)
AspectJ切面Spring实现了自己的AOP(符合aop联盟标准)
AspectJ切点表达式
args() 限制连接点匹配参数为指定类型的执行方法(即参数为某类型)
@args() 限制连接点匹配参数为指定注解标注的执行方法(即参数带有某注解的方法)
execution() 用于匹配是连接点的执行方法
within() 限制连接点匹配指定的类型
@within() 限制连接点匹配指定注解所标注的类型(使用Spring AOP时,方法定义在由指定注解标注的类里)
@annotation 限定匹配带有指定注解的连接点(比如限定被xx注解标注的方法)
同时切点和切点直接也可以组合,组合表达式:&& || !
ProxyFactoryBean
<bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interfaces">
<list>
<value>io.dirac.aop.HelloService</value>
</list>
</property>
<property name="target" ref="hello"/>
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
<!-- proxy target -->
<bean id="hello" class="io.dirac.aop.HelloServiceImpl"/>
<!-- AdvisorLogger implements MethodInterceptor 在这里面实现前后通知 -->
<bean id="advisor" class="io.dirac.aop.AdvisorLogger"/>
<aop:config>
<bean id="logger" class="io.dirac.aop.LoggerHandler"/>
<!-- 对于log这个切面,会拦截HelloService中say方法这个切点id=sayMethod,say之前调用log切面的preHandle和postHandle方法 -->
<aop:config>
<aop:aspect id="log" ref="logger">
<aop:pointcut id="sayMethod" expression="execution(* io.dirac.aop.HelloService.say(..))"/>
<aop:before method="preHandle" pointcut-ref="sayMethod"/>
<aop:after method="postHandle" pointcut-ref="sayMethod"/>
</aop:aspect>
</aop:config>
使用的是MethodInterceptor
org.springframework.aop.aspectj.AspectJMethodBeforeAdvice
@Aspect注解使用的是MethodInvocation,然后创建代理
Spring AOP的开启,如果是XML配置,需要配置如下开启<aop:aspectj-autoproxy/>
, 如果是JavaConfig,需要在配置类上@EnableAspectJAutoProxy
开启。
@Aspect
@Component
public class ConcurrencyLimitAspect {
// 值就是连接点的具体形式Pointcut,只不过此处现在一起了
// 对于AspectJ而言,连接点可以不止是方法
// 这个切点的意思是:对于注解了Component类中注解了ConcurrencyLimit方法起作用,写切面在方法上做并发控制
@Around("@annotation(limit) && @within(org.springframework.stereotype.Component)")
public Object limit(ProceedingJoinPoint point, ConcurrencyLimit limit) {
int lmt = limit.limit();
if (lmt < 0) {
throw new RuntimeException("limit!");
}
try {
Object ret = point.proceed();
return ret;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
// 在createService方法处连接点的切点
@Pointcut("execution(public * io.dirax.api.OrderService.createService(..))")
public void pointcut() {
}
// pointcut切点前置通知
@Before("pointcut()")
public void beforeAction(JoinPoint jp) {
// TODO
}
// 指定参数,可以在通知中把参数传入(如果对参数request修改,会影响后续)
@Pointcut("execution(public * io.dirax.api.OrderService.createService(Object)) && args(request)")
public void pointcutWithArgs(Object request) {
}
@After(value = "pointcut(request)", argNames = "request")
public void after(Object request){
//TODO
}
}
对环绕(@Around
)通知,参数ProceedingJoinPoint
可以在实际方法前后做环绕处理;其他的可以使用JoinPoint
。
如上实例,对于注解了Component
的对象中注解了ConcurrencyLimit
的方法,会使用切面ConcurrencyLimitAspect
中的limit
做增强。
Spring AOP原理
首先要牢记AOP的几个概念,org.aopalliance.intercept和org.aopalliance.aop包下的接口定义(spring的包)。
- Joinpoit 连接点,连接的方法,即需要被拦截的方法
- Pointcut 切点,用于选择连接点是否匹配,AspectJExpressionPointcut最终具备了选择AspectJ风格切点表达式的能力
- Advice 通知,即横切逻辑,在连接点前置/后置/环绕等通知执行的操作
- Aspect 切面,整合了切点(where)和通知(when、how),解决了对什么方法在何时执行什么样的操作问题
- Weaving 织入,将通知逻辑插入(增强)到目标对象的方法上,通过BeanPostProcessor对初始化完的bean进行处理,判断是否要包装,如果需要,则生成AOP代理
看代码时有个点
连接点Jointpoint,对应的是MethodInvocation
通知Advice,对应的是InvocationInterceptor