上篇博客我们介绍了创建代理对象:白话Spring源码(十一):Spring AOP源码分析-创建代理对象。现在我们的得到了 bean 的代理对象,且通知也以合适的方式插在了目标方法的前后。接下来要做的事情,就是执行通知逻辑了。通知可能在目标方法前执行,也可能在目标方法后执行。具体的执行时机,取决于用户的配置。当目标方法被多个通知匹配到时,Spring 通过引入拦截器链来保证每个通知的正常执行。在本文中,我们将会通过源码了解到 Spring 的拦截器链的执行过程。
JDK 动态代理逻辑分析
我来分析一下 JDK 动态代理逻辑。对于 JDK 动态代理,代理逻辑封装在 InvocationHandler 接口实现类的 invoke 方法中。JdkDynamicAopProxy 实现了 InvocationHandler 接口,下面我们就来分析一下 JdkDynamicAopProxy 的 invoke 方法。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation = null;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = advised.targetSource;
Class targetClass = null;
Object target = null;
try {
// Try special rules for equals() method and implementation of the
// Advised AOP configuration interface
// Short-circuit expensive Method.equals() call, as Object.equals() isn't overloaded
if (method.getDeclaringClass() == Object.class && "equals".equals(method.getName())) {
// What if equals throws exception!?
// This class implements the equals() method itself
return new Boolean(equals(args[0]));
}
else if (Advised.class == method.getDeclaringClass()) {
// Service invocations on ProxyConfig with the proxy config
return AopProxyUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal = null;
// May be null. Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
if (this.advised.exposeProxy) {
// Make invocation available if necessary
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get the interception chain for this method
List chain = this.advised.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this.advised, proxy, method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on
// direct reflective invocation of the target, and avoid creating a MethodInvocation
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying
retVal = AopProxyUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// We need to create a method invocation...
//invocation = advised.getMethodInvocationFactory().getMethodInvocation(proxy, method, targetClass, target, args, chain, advised);
invocation = new ReflectiveMethodInvocation(proxy, target,
method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain
retVal = invocation.proceed();
}
// Massage return value if necessary
if (retVal != null && retVal == target) {
// Special case: it returned "this"
// Note that we can't help if the target sets
// a reference to itself in another returned object
retVal = proxy;
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy
AopContext.setCurrentProxy(oldProxy);
}
//if (invocation != null) {
// advised.getMethodInvocationFactory().release(invocation);
//}
}
}
如上,上面的代码我做了比较详细的注释。下面我们来总结一下 invoke 方法的执行流程,如下:
- 检测 expose-proxy 是否为 true,若为 true,则暴露代理对象
- 获取适合当前方法的拦截器
- 如果拦截器链为空,则直接通过反射执行目标方法
- 若拦截器链不为空,则创建方法调用 ReflectiveMethodInvocation 对象
- 调用 ReflectiveMethodInvocation 对象的 proceed() 方法启动拦截器链
- 处理返回值,并返回该值
在以上6步中,我们重点关注第2步和第5步中的逻辑。第2步用于获取拦截器链,第5步则是启动拦截器链。下面先来分析获取拦截器链的过程。
获取所有的拦截器
所谓的拦截器,顾名思义,是指用于对目标方法的调用进行拦截的一种工具。拦截器的源码比较简单,所以我们直接看源码好了。下面以前置通知拦截器为例,如下:
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
/** 前置通知 */
private MethodBeforeAdvice advice;
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
// 执行前置通知逻辑
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
// 通过 MethodInvocation 调用下一个拦截器,若所有拦截器均执行完,则调用目标方法
return mi.proceed();
}
}
如上,前置通知的逻辑在目标方法执行前被执行。这里先简单向大家介绍一下拦截器是什么,。本节我们先来看看如何如何获取拦截器,如下:
public List getInterceptorsAndDynamicInterceptionAdvice(Advised config, Object proxy, Method method, Class targetClass) {
List cached = (List) this.methodCache.get(method);
if (cached == null) {
// Recalculate
cached = AdvisorChainFactoryUtils.calculateInterceptorsAndDynamicInterceptionAdvice(config, proxy, method, targetClass);
this.methodCache.put(method, cached);
}
return cached;
}
public static List calculateInterceptorsAndDynamicInterceptionAdvice(Advised config, Object proxy, Method method, Class targetClass) {
List interceptors = new ArrayList(config.getAdvisors().length);
for (int i = 0; i < config.getAdvisors().length; i++) {
Advisor advisor = config.getAdvisors()[i];
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
MethodInterceptor interceptor = (MethodInterceptor) GlobalAdvisorAdapterRegistry.getInstance().getInterceptor(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (mm.matches(method, targetClass)) {
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptor() method
// isn't a problem as we normally cache created chains
interceptors.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm) );
}
else {
interceptors.add(interceptor);
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (ia.getClassFilter().matches(targetClass)) {
MethodInterceptor interceptor = (MethodInterceptor) GlobalAdvisorAdapterRegistry.getInstance().getInterceptor(advisor);
interceptors.add(interceptor);
}
}
} // for
return interceptors;
} // calculateInterceptorsAndDynamicInterceptionAdvice
以上就是获取拦截器的过程,代码有点长,不过好在逻辑不是很复杂。这里简单总结一下以上源码的执行过程,如下:
- 从缓存中获取当前方法的拦截器链
- 若缓存未命中,则调用 AdvisorChainFactoryUtils.calculateInterceptorsAndDynamicInterceptionAdvice获取拦截器链
- 遍历通知器列表
- 对于 PointcutAdvisor 类型的通知器,这里要调用通知器所持有的切点(Pointcut)对类和方法进行匹配,匹配成功说明应向当前方法织入通知逻辑
- 调用 getInterceptors 方法对非 MethodInterceptor 类型的通知进行转换
- 返回拦截器数组,并在随后存入缓存中
这里需要说明一下,部分通知器是没有实现 MethodInterceptor 接口的,这里使用了适配器模式,如下:
public Interceptor getInterceptor(Advisor advisor) throws UnknownAdviceTypeException {
Advice advice = advisor.getAdvice();
if (advice instanceof Interceptor) {
return (Interceptor) advice;
}
for (int i = 0; i < this.adapters.size(); i++) {
AdvisorAdapter adapter = (AdvisorAdapter) this.adapters.get(i);
if (adapter.supportsAdvice(advice)) {
return adapter.getInterceptor(advisor);
}
}
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
适配器接口:
public interface AdvisorAdapter {
boolean supportsAdvice(Advice advice);
Interceptor getInterceptor(Advisor advisor);
}
适配器实现类:
我们看一个BeforeAdviceAdapter:
class BeforeAdviceAdapter implements AdvisorAdapter {
/**
* @see org.springframework.aop.framework.adapter.AdvisorAdapter#supportsAdvice(java.lang.Object)
*/
public boolean supportsAdvice(Advice advice) {
return advice instanceof MethodBeforeAdvice;
}
/**
* @see org.springframework.aop.framework.adapter.AdvisorAdapter#getInterceptor(org.springframework.aop.Advisor)
*/
public Interceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice) ;
}
}
现在我们已经获得了拦截器链,那接下来要做的事情就是启动拦截器了。所以接下来,我们一起去看看 Sring 是如何让拦截器链运行起来的。
启动拦截器链
我们先来说说 ReflectiveMethodInvocation。ReflectiveMethodInvocation 贯穿于拦截器链执行的始终,可以说是核心。该类的 proceed 方法用于启动启动拦截器链,下面我们去看看这个方法的逻辑。
public class ReflectiveMethodInvocation implements MethodInvocation {
protected Method method;
protected Object[] arguments;
protected Object target;
protected Object proxy;
/**
* List of Methodnterceptor and InterceptorAndDynamicMethodMatcher that need dynamic checks.
**/
protected List interceptorsAndDynamicMethodMatchers;
/**
* Index from 0 of the current interceptor we're invoking.
* -1 until we invoke: then the current interceptor
*/
private int currentInterceptorIndex = -1;
private Class targetClass;
/**
* Construct a new MethodInvocation with given arguments
* @param interceptorsAndDynamicMethodMatchers interceptors that should be applied,
* along with any InterceptorAndDynamicMethodMatchers that need evaluation at runtime.
* MethodMatchers included in this struct must already have been found to have matched as far
* as was possibly statically. Passing an array might be about 10% faster, but would complicate
* the code. And it would work only for static pointcuts.
*/
public ReflectiveMethodInvocation(Object proxy, Object target,
Method m, Object[] arguments,
Class targetClass, List interceptorsAndDynamicMethodMatchers) {
this.proxy = proxy;
this.target = target;
this.targetClass = targetClass;
this.method = m;
this.arguments = arguments;
this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
}
/**
* Return the method invoked on the proxied interface.
* May or may not correspond with a method invoked on an underlying
* implementation of that interface.
* @return Method
*/
public final Method getMethod() {
return this.method;
}
public final AccessibleObject getStaticPart() {
return this.method;
}
/**
* Return the proxy that this interception was made through
* @return Object
*/
public final Object getProxy() {
return this.proxy;
}
/**
* Private optimization method
* @return Object[]
*/
public final Object[] getArguments() {
return this.arguments;
}
/**
* @see org.aopalliance.intercept.Invocation#proceed
*/
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match
InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed
// Skip this interceptor and invoke the next in the chain
return proceed();
}
}
else {
// It's an interceptor so we just invoke it: the pointcut will have
// been evaluated statically before this object was constructed
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
/**
* Invoke the joinpoint using reflection. Subclasses can override this to use custom
* invocation.
* @return the return value of the joinpoint
* @throws Throwable if invoking the joinpoint resulted in an exception
*/
protected Object invokeJoinpoint() throws Throwable {
return AopProxyUtils.invokeJoinpointUsingReflection(target, method, arguments);
}
/**
* @see org.aopalliance.intercept.Invocation#getThis
*/
public final Object getThis() {
return this.target;
}
public String toString() {
// Don't do toString on target, it may be
// proxied
// ToString on args may also fail
String s = "Invocation: method=[" + method + "] " +
//"args=[" + StringUtils.arrayToDelimitedString(arguments, ",") +
"args=" + this.arguments +
"] ";
s += (this.target == null) ? "target is null":
"target is of class " + target.getClass().getName();
return s;
}
}
这个类我们主要关注proceed方法,这里使用了责任链模式,关于责任链模式请看:责任链模式(Chain of Responsibility)。
好了,到现在Spring AOP源码基本就介绍完了。谢谢大家!