在这两篇文章中,我们学会了借助Spring 自动代理,定义自己的Advisor来增强Bean。
SpringBoot2.x下“AOP自动代理”实战—注解版日志打印
SpringBoot2.x下“后置处理器”完成AOP代理实战—注解版日志打印
但是小伙伴在SpringBoot2.x下“后置处理器”完成AOP代理实战—注解版日志打印中却发现,Spring使用MethodInterceptor来充当Advice。
public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor
implements InitializingBean {
private Class<? extends Annotation> validatedAnnotationType = Validated.class;
...
//定义一个Advisor使用的是pointcut切点和Advice
@Override
public void afterPropertiesSet() {
Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);
this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator));
}
//这个方法返回的却是MethodValidationInterceptor对象。
protected Advice createMethodValidationAdvice(@Nullable Validator validator) {
return (validator != null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor());
}
}
显而易见,MethodInterceptor是Advice的子类。
但是我们在实战中却是使用的MethodBeforeAdvice,看上去并未和MethodInterceptor有交集。在执行AOP代理增强方法时,是执行Advice还是执行MethodInterceptor???

那么Advice和MethodInterceptor到底是什么关系?我们在实际扩展AOP时,是使用Advice还是使用MethodInterceptor做通知???
1. MethodInterceptor结构
/**
* 用户可以实现MethodInvocation接口,来修原始的行为,使用方式如下
*
* <pre class=code>
* class TracingInterceptor implements MethodInterceptor {
* Object invoke(MethodInvocation i) throws Throwable {
* System.out.println("method "+i.getMethod()+" is called on "+
* i.getThis()+" with args "+i.getArguments());
* Object ret=i.proceed();
* System.out.println("method "+i.getMethod()+" returns "+ret);
* return ret;
* }
* }
* </pre>
*
* @author Rod Johnson
*/
@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
//执行invocation之前和之后可以做一些额外的处理
Object invoke(MethodInvocation invocation) throws Throwable;
}
感觉MethodInterceptor才真正完成对方法的增强。
2. 将Advice转换为MethodInterceptor
以Cglib代理为例:
以SpringBoot2.x下“后置处理器”完成AOP代理实战—注解版日志打印实战代码为例:

debug代码入口:org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept

将代码定位到
org.springframework.aop.framework.DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice下。
该方法获取到代理对象所有的Advisor,使用Pointcut对method进行匹配。若匹配成功(即可以增强该方法)。
抽取关键代码:Advice转换为MethodInterceptor关键代码
@Test
public void test() {
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
Pointcut pointcut = AnnotationMatchingPointcut.forMethodAnnotation(MyLogAnno.class);
Advisor advisor = new DefaultPointcutAdvisor(pointcut, (MethodBeforeAdvice)(method, args, target) - >{
//打印日志
log.info("调用方法:[{}]", method);
log.info("方法参数:[{}]", JSON.toJSONString(args));
log.info("target类:[{}]", target);
});
//将Advisor转换为MethodInterceptor[]数组对象
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
}
获取Advice将其转换为MethodBeforeAdviceInterceptor:
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
//获取到advice将其转换为MethodBeforeAdviceInterceptor
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
private final MethodBeforeAdvice advice;
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
//前置通知是在mi.proceed()前面调用advice的逻辑。
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
}
注意:mi.proceed();方法实际上是调用的org.springframework.aop.framework.ReflectiveMethodInvocation#proceed方法。该方法在List列表(过滤器链)中循环取出MethodInterceptor对象执行invoke方法(invoke方法又会调用proceed方法,直至链表为空)。
在滤器链空的情况时,执行并返回invokeJoinpoint();。(执行连接点的代码,即target方法代码)。
总结:在调用代理方法时,Advice会被转换为MethodInterceptor对象。invoke方法和proceed方法相互调用,完成功能增强!
3. 实战—改造注解版日志打印
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyLogAnno {
Class<?>[] value() default {};
}
直接使用MethodInterceptor作为Advice进行通知。
@Slf4j
@Component
public class MyLogAnnoBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor
implements InitializingBean {
private Class<? extends Annotation> myLogAnnoType = MyLogAnno.class;
@Override
public void afterPropertiesSet() throws Exception {
//对带有myLogAnno注解的方法进行拦截
Pointcut pointcut = AnnotationMatchingPointcut.forMethodAnnotation(myLogAnnoType);
this.advisor = new DefaultPointcutAdvisor(pointcut,
(MethodInterceptor) (invocation) -> {
//打印日志
log.info("调用方法:[{}]", invocation.getMethod());
log.info("方法参数:[{}]", JSON.toJSONString(invocation.getArguments()));
log.info("target类:[{}]", invocation.getThis());
//调用拦截器链的其他方法
return invocation.proceed();
});
}
}
