白话Spring源码(十二):Spring AOP源码分析-拦截器链的执行过程

上篇博客我们介绍了创建代理对象:白话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 方法的执行流程,如下:

  1. 检测 expose-proxy 是否为 true,若为 true,则暴露代理对象
  2. 获取适合当前方法的拦截器
  3. 如果拦截器链为空,则直接通过反射执行目标方法
  4. 若拦截器链不为空,则创建方法调用 ReflectiveMethodInvocation 对象
  5. 调用 ReflectiveMethodInvocation 对象的 proceed() 方法启动拦截器链
  6. 处理返回值,并返回该值

在以上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

以上就是获取拦截器的过程,代码有点长,不过好在逻辑不是很复杂。这里简单总结一下以上源码的执行过程,如下:

  1. 从缓存中获取当前方法的拦截器链
  2. 若缓存未命中,则调用 AdvisorChainFactoryUtils.calculateInterceptorsAndDynamicInterceptionAdvice获取拦截器链
  3. 遍历通知器列表
  4. 对于 PointcutAdvisor 类型的通知器,这里要调用通知器所持有的切点(Pointcut)对类和方法进行匹配,匹配成功说明应向当前方法织入通知逻辑
  5. 调用 getInterceptors 方法对非 MethodInterceptor 类型的通知进行转换
  6. 返回拦截器数组,并在随后存入缓存中

这里需要说明一下,部分通知器是没有实现 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源码基本就介绍完了。谢谢大家!

猜你喜欢

转载自blog.csdn.net/haoxin963/article/details/89299252