写在前面
本篇不准备对着源码一步一步讲解这一过程,而是准备先剖析一下这一过程中所用到的一些类和方法的功能,当这些“螺丝”都熟悉之后,再一步一步看源码的时候,便会畅通无阻。这也是我在阅读源码过程中的感觉,因为spring的整套源码一环套一环,流程很长,可能跟着跟着就不知所云了。但是如果我们分解一长串流程,一步一步去拆解,最后串联起来的那一刻就会恍然大悟。
相关知识
AOP代理增强时机
bean的创建过程中的初始化阶段的后置处理(postProcessAfterInitialization),在满足条件的情况下会对bean进行AOP增强。核心实现就是AbstractAutoProxyCreator的wrapIfNecessary方法。该方法主要逻辑实现就是,找到容器中能够应用到当前所创建的bean的切面,利用切面为bean创建代理对象。
AOP的一些概念
在真正介绍拦截器链之前,先理清一下一些我自己第一次看源码时比较模糊的概念。
切面:对主业务逻辑的一种增强。spring中的Advice和Advisor都是切面的一种实现,只不过Advisor相比Advice能够实现更复杂的逻辑。
织入:将切面应用到目标方法或类的过程。比如cglib的intercept方法,jdk代理的invoke方法,其完成的逻辑都可以叫做织入。
连接点:可以被切面织入的方法。
切点:具体被切面织入的方法。
连接点和切点什么区别呢?一个修饰词是“可以”,一个修饰词是“具体”。在我看来,切点规定了哪些方法将被切面增强,满足切点的限定条件都将得到增强。而连接点表示了满足切点扫描的方法的集合,这些方法可能有些满足切点的限定条件,有些不满足。
来看一下spring中对切点的定义是什么:
public interface Pointcut {
/**
* Return the ClassFilter for this pointcut.
* @return the ClassFilter (never {@code null})
*/
ClassFilter getClassFilter();
/**
* Return the MethodMatcher for this pointcut.
* @return the MethodMatcher (never {@code null})
*/
MethodMatcher getMethodMatcher();
/**
* Canonical Pointcut instance that always matches.
*/
Pointcut TRUE = TruePointcut.INSTANCE;
}
其中的ClassFilter和MethodMatcher都具有matches方法,决定了哪些类或方法满足切点的限定条件。
把这些概念串起来,我总结就是:
spring的AOP就是把切面(Advice、Advisor)织入(Weaving)到满足切点(PointCut)限定条件的连接点(JoinPoint)的过程。
AOP拦截链
下面开始正菜了,在spring中,我们怎么用切面去增强一个类呢?是拦截!我们拦截方法的执行,去添加一些额外的逻辑。那如何进行拦截呢?当然是动态代理了。
在spring的整个生态中,强依赖动态代理,所以,最最基础的cglib代理和jdk代理是我们学习spring的必备基础。spring有多强依赖动态代理呢,对于cglib代理来说,spring直接copy了一份cglib代理的源码到自己的框架中。以至于你去搜MethodInterceptor会发现有两个相同的类,一个是在net.sf.cglib.proxy包下,一个是在org.springframework.cglib.proxy包下。
spring是如何对方法进行拦截呢?答案是通过MethodInterceptor。
public interface MethodInterceptor extends Interceptor {
/**
* Implement this method to perform extra treatments before and
* after the invocation. Polite implementations would certainly
* like to invoke {@link Joinpoint#proceed()}.
* @param invocation the method invocation joinpoint
* @return the result of the call to {@link Joinpoint#proceed()};
* might be intercepted by the interceptor
* @throws Throwable if the interceptors or the target object
* throws an exception
*/
Object invoke(MethodInvocation invocation) throws Throwable;
}
也就是说,被增强类的方法执行时,实际是通过MethodInterceptor#invoke被调用的。
需要注意的是,用于AOP拦截的MethodInterceptor与cglib代理增强的MethodInterceptor虽然类名相同,但是却是完全不同的两个类。这里可以过度解读一下spring的命名(spring的命名是一种艺术)。cglib代理依靠MethodInterceptor#intercept方法实现,jdk代理依靠InvocationHandler#invoke方法实现。而spring AOP实现拦截是依靠MethodInterceptor#invoke方法。注意到没有,这是综合了cglib代理的类名和jdk代理的方法名。
现在我们新建两个拦截器,LogInterceptor和TimeInterceptor,分别用于记录日志和计时。
LogInterceptor
public class LogInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("开始执行");
Object retVal = invocation.proceed();
System.out.println("执行完毕");
return retVal;
}
}
TimeInterceptor
public class TimeInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("计时开始");
Object retVal = invocation.proceed();
System.out.println("计时结束,耗时:" + (System.currentTimeMillis() - start) / 1000);
return retVal;
}
}
同时我们再新建两个spring自己提供的切面Advice,分别是MethodBeforeAdvice和AfterReturningAdvice。
AOPAfterReturningAdvice
public class AOPAfterReturningAdvice implements AfterReturningAdvice {