Spring AOP源码分析(四)Spring AOP的JDK动态代理
Spring AOP源码分析(四)Spring AOP的JDK动态代理
乒乓狂魔 发表于3年前
Spring AOP源码分析(四)Spring AOP的JDK动态代理
  • 发表于 3年前
  • 阅读 587
  • 收藏 1
  • 点赞 0
  • 评论 0

新睿云服务器60天免费使用,快来体验!>>>   

本篇文章将会介绍上一个例子中的源码执行情况,从中熟悉整个SpringAOP的一些概念和接口设计。

首先整个SpringAOP的分两大过程。
第一个过程:根据xml文件或者注解中配置的拦截信息,生成相应的代理对象,这个代理对象包含了对应的拦截器。
第二个过程:执行所拦截的方法时,就是调用代理对象的执行逻辑,完成各种拦截。

本文章先对第二个过程进行源码解析。
对第一个过程先做简单概述,如果拦截的类的对应方法是接口方法,则使用JDK的Proxy进行代理对象的创建否则使用CGLIB进行代理对象的创建。

本工程采用的之前文章所给出案例链接:http://www.iteye.com/topic/336873

简单概述如下:
拦截类:TestAspect

public void doAfter(JoinPoint jp) {  
        System.out.println("log Ending method: "  
                + jp.getTarget().getClass().getName() + "."  
                + jp.getSignature().getName());  
    }  
  
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {  
        long time = System.currentTimeMillis();  
        Object retVal = pjp.proceed();  
        time = System.currentTimeMillis() - time;  
        System.out.println("process time: " + time + " ms");  
        return retVal;  
    }  
  
    public void doBefore(JoinPoint jp) {  
        System.out.println("log Begining method: "  
                + jp.getTarget().getClass().getName() + "."  
                + jp.getSignature().getName());  
    }  
  
    public void doThrowing(JoinPoint jp, Throwable ex) {  
        System.out.println("method " + jp.getTarget().getClass().getName()  
                + "." + jp.getSignature().getName() + " throw exception");  
        System.out.println(ex.getMessage());  
    }

xml的配置:
<aop:config>  
        <aop:aspect id="TestAspect" ref="aspectBean">  
        	
            <!-- 配置com.spring.service包下所有类或接口的所有方法   -->
            <aop:pointcut id="businessService"  
                expression="execution(* com.lg.aop.service.*.*(..))" />  
            <aop:before pointcut-ref="businessService" method="doBefore"/>  
            <aop:after pointcut-ref="businessService" method="doAfter"/>  
            <aop:around pointcut-ref="businessService" method="doAround"/>
            <aop:after-throwing pointcut-ref="businessService" method="doThrowing" throwing="ex"/>  
        </aop:aspect>  
    </aop:config>

建立单元测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/mvc-servlet.xml")
public class AOPTest {

	@Autowired
	private AService aService;
	@Autowired
	private BServiceImpl bServiceImpl;
	
	@Test
	public void testAOP(){
		aService.barA();
		bServiceImpl.fooB();
	}
	
}

运行,效果如下:
log Begining method: com.lg.aop.service.impl.AServiceImpl.barA
AServiceImpl.barA()
process time: 0 ms
log Ending method: com.lg.aop.service.impl.AServiceImpl.barA
log Begining method: com.lg.aop.service.BServiceImpl.fooB
BServiceImpl.fooB()
process time: 12 ms
log Ending method: com.lg.aop.service.BServiceImpl.fooB

接下来就是分析这一过程。
首先我们会看到:此时的AService不再是它的实现者AServiceImpl,而是一个代理对象。
由于barA()是接口方法,所以会选择使用JDK的Proxy进行代理对象的创建。如下在JdkDynamicAopProxy中:

@Override
	public Object getProxy(ClassLoader classLoader) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
		}
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}

来看下Proxy.newProxyInstance(classLoader, proxiedInterfaces, this)(详情见上一篇文章http://lgbolgger.iteye.com/blog/2117215)这个代码就是创建代理对象,第一个参数指定classLoader,第二个参数指定代理对象要实现的接口,第三个对象那个是InvocationHandler类型。来看下InvocationHandler:
public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

一个代理对象和一个InvocationHandler绑定,当执行代理对象的方法时,就会去执行InvocationHandler的invoke(Object proxy, Method method, Object[] args)方法,在该方法中你可以选择相应的处理或者不执行代理对象的method方法。
JdkDynamicAopProxy继承了InvocationHandler,所以上文中在创建代理对象时传的参数是this,即这个代理对象的方法的执行拦截情况全部在JdkDynamicAopProxy的invoke(Object proxy, Method method, Object[] args)方法中。
我们先来了解下JdkDynamicAopProxy的一些属性:

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
	/** Config used to configure this proxy */
	private final AdvisedSupport advised;

	private boolean equalsDefined;
	private boolean hashCodeDefined;
        //略
}

最关键的就是这个AdvisedSupport advised属性,它包含了我们在xml中配置的拦截信息,同时还包含了这个JdkDynamicAopProxy要代理的接口及其实现类,对于本文来说就是AService和AServiceImpl。JdkDynamicAopProxy可以根据这些配置信息来创建一个代理对象实现拦截,同时又可以执行AServiceImpl本身的业务方法。

AdvisedSupport有两个重要的内容:TargetSource和List<Advisor> advisors和List<Class<?>> interfaces。
TargetSource是目标类型和目标对象的包裹,在这里是AServiceImpl类和AServiceImpl对象。
List<Class<?>> interfaces:包含了目标类型实现的接口,在这里就是AService
List<Advisor> advisors:这里包含了我们在xml文件中配置的所有信息。这一部分是每个AdvisedSupport所共享的信息,而前面两个是每个AdvisedSupport所独有的信息。

在详细看下AdvisedSupport:

public class AdvisedSupport extends ProxyConfig implements Advised

接口Advised:主要包含TargetSource和List<Advisor> advisors和List<Class<?>> interface
ProxyConfig:则是对要产生的代理对象的一些配置,如下:

public class ProxyConfig implements Serializable {

	/** use serialVersionUID from Spring 1.2 for interoperability */
	private static final long serialVersionUID = -8409359707199703185L;


	private boolean proxyTargetClass = false;

	private boolean optimize = false;

	boolean opaque = false;

	boolean exposeProxy = false;

	private boolean frozen = false;
}

其中proxyTargetClass:表示是否强制使用CGLIB进行代理对象的创建
exposeProxy :表示是否暴漏代理对象,实现线程内共享,这里又是使用ThreadLocal模式。
他们分别对应xml配置中的

<aop:config expose-proxy="false" proxy-target-class="false"> 
</aop:config>

继续回到AdvisedSupport ,对于它的List<Advisor> advisors则分别对应xml中的配置:
<aop:before pointcut-ref="businessService1" method="doBefore" />  
<aop:after pointcut-ref="businessService2" method="doAfter"/>  
<aop:around pointcut-ref="businessService2" method="doAround"/>
<aop:after-throwing pointcut-ref="businessService1" method="doThrowing" throwing="ex"/>

则产生的Advisor如下:每一个都是AspectJPointcutAdvisor对象,该对象所包含的内容如下:
private final AbstractAspectJAdvice advice;
private final Pointcut pointcut;
private Integer order;

分别和xml配置中的内容相对应,在xml中你还可以指定order值,用来排序,这个顺序关系到这些拦截方法的执行顺序,之后我们会详细分析这个拦截器链的执行情况。
如aop:before产生的AspectJPointcutAdvisor的AbstractAspectJAdvice 为AspectJMethodBeforeAdvice,Pointcut 为ComposablePointcut。具体的内容已在上文中接口说明中给出了说明。

至此xml中的配置对应到AdvisedSupport基本上简单的了解了,这些内容的创建都是为下文方法的拦截做准备。
下面继续回到JdkDynamicAopProxy,来看看拦截过程,即调用代理对象的方法,然后被拦截到代理对象的InvocationHandler的invoke方法,JdkDynamicAopProxy的invoke方法如下:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		MethodInvocation invocation;
		Object oldProxy = null;
		boolean setProxyContext = false;

		TargetSource targetSource = this.advised.targetSource;
		Class<?> targetClass = null;
		Object target = null;

		try {
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				// The target does not implement the equals(Object) method itself.
				return equals(args[0]);
			}
			if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				// The target does not implement the hashCode() method itself.
				return hashCode();
			}
			if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				// Service invocations on ProxyConfig with the proxy config...
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}

			Object retVal;

//我们关注的重点1
			if (this.advised.exposeProxy) {
				// Make invocation available if necessary.
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

			// 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();
			}
//关注的重点2
			// Get the interception chain for this method.
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(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.
//关注的重点3
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
			}
			else {
//关注的重点4
				// We need to create a method invocation...
				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.
			Class<?> returnType = method.getReturnType();
			if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
				// Special case: it returned "this" and the return type of the method
				// is type-compatible. Note that we can't help if the target sets
				// a reference to itself in another returned object.
				retVal = proxy;
			} else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
				throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);
			}
			return retVal;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				// Must have come from TargetSource.
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				// Restore old proxy.
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}

关注的重点1:this.advised.exposeProxy即我们在xml文件中所配置的<aop:config expose-proxy="false">。如果配置为true,默认false,则意味着在该线程内将会暴露proxy代理对象,实现共享,即在该线程中的任何地方都可以都可以取到proxy代理对象。具体是由ThreadLocal设计模式来实现的,可以见我的另一篇博客对ThreadLocal设计模式的分析(http://lgbolgger.iteye.com/blog/2117216),来看下AopContext:
private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<Object>("Current AOP proxy");

public static Object currentProxy() throws IllegalStateException {
		Object proxy = currentProxy.get();
		if (proxy == null) {
			throw new IllegalStateException(
					"Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.");
		}
		return proxy;
	}

static Object setCurrentProxy(Object proxy) {
		Object old = currentProxy.get();
		if (proxy != null) {
			currentProxy.set(proxy);
		}
		else {
			currentProxy.remove();
		}
		return old;
	}

AopContext内部使用了一个ThreadLocal<Object> currentProxy,它的两个方法都是静态方法,任何线程都可以调用这两个方法,当线程一调用setCurrentProxy方法时,AopContext的currentProxy就会去操作线程一内部的数据,当线程二调用setCurrentProxy方法时,AopContext的currentProxy就会去操作线程二内部的数据,互不干扰。这种情况不会引起多线程争抢资源数据的情况,同时实现了在某个线程中实现的数据的共享,而不用在某个线程中来回的传递参数。这就是ThreadLocal的设计模式,对于ThreadLocal<Object> currentProxy这样的类型属性,它仅仅是操作调用currentProxy的方法的当前线程的工具类,仅此而已。

继续,这样的话就可以实现了在本线程中共享proxy代理对象,这就意味着我们在我们自定义的advice上通过AopContext可以获取到当前的代理对象。
关注的重点2:根据我们的目标类和方法找到对应的拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
它内部是通过advised的一个this.advisorChainFactory来实现这一过程,advisorChainFactory默认为DefaultAdvisorChainFactory,实现过程如下:

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, Class<?> targetClass) {

		// This is somewhat tricky... we have to process introductions first,
		// but we need to preserve order in the ultimate list.
		List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
		boolean hasIntroductions = hasMatchingIntroductions(config, targetClass);
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
		for (Advisor advisor : config.getAdvisors()) {
			if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
					MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {
						if (mm.isRuntime()) {
							// Creating a new object instance in the getInterceptors() method
							// isn't a problem as we normally cache created chains.
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				}
			}
			else if (advisor instanceof IntroductionAdvisor) {
				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
				if (config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {
					Interceptor[] interceptors = registry.getInterceptors(advisor);
					interceptorList.addAll(Arrays.asList(interceptors));
				}
			}
			else {
				Interceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}
		return interceptorList;
	}

上述过程分了三种情况来获取对应的Interceptor拦截器,config.getAdvisors()是我们在xml文件中所配置的所有的拦截情况,对于这些所有的拦截情况:

当Advisor为PointcutAdvisor类型的时:
这是我们本工程的配置的拦截,每个拦截都有pointcut,针对这种情况,首先判断该PointcutAdvisor的ClassFilter是否拦截了targetClass,若拦截则需继续判断PointcutAdvisor的MethodMatcher是否拦截targetClass的method方法。如果也拦截了,就需要将PointcutAdvisor的adice添加进去,则继续判断这个PointcutAdvisor的MethodMatcher是否是动态变化的,若是则需要将interceptor进一步包装成InterceptorAndDynamicMethodMatcher然后添加进去。

当Advisor为IntroductionAdvisor类型的时候:
IntroductionAdvisor应用在类上,不需要判断是否拦截了相应的方法。IntroductionAdvisor只有一个ClassFilter。此时仅仅去判断下是否拦截相应的类即可。

其他情况:
直接获取相应的interceptor。

我们来看下根据Advisor来获取对应的MethodInterceptor方法:

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
		List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
		Advice advice = advisor.getAdvice();
		if (advice instanceof MethodInterceptor) {
			interceptors.add((MethodInterceptor) advice);
		}
		for (AdvisorAdapter adapter : this.adapters) {
			if (adapter.supportsAdvice(advice)) {
				interceptors.add(adapter.getInterceptor(advisor));
			}
		}
		if (interceptors.isEmpty()) {
			throw new UnknownAdviceTypeException(advisor.getAdvice());
		}
		return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
	}

首先是判断advisor.getAdvice()是否已实现了MethodInterceptor,如AspectJAfterAdvice、AspectJAfterThrowingAdvice等。
然后又是利用适配器模式,将不用的advice封装成对应的MethodInterceptor。如MethodBeforeAdviceAdapter,默认硬编码注册了三个

public DefaultAdvisorAdapterRegistry() {
		registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
		registerAdvisorAdapter(new AfterReturningAdviceAdapter());
		registerAdvisorAdapter(new ThrowsAdviceAdapter());
	}

看下MethodBeforeAdviceAdapter:
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

	@Override
	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof MethodBeforeAdvice);
	}

	@Override
	public MethodInterceptor getInterceptor(Advisor advisor) {
		MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
		return new MethodBeforeAdviceInterceptor(advice);
	}

}

这就是典型的适配器模式,当Advice为MethodBeforeAdvice时,就会封装成MethodBeforeAdviceInterceptor。

至此获取MethodInterceptor链的过程就完成了,回到List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);即List<Object>是一系列的MethodInterceptor构成的。

继续看JdkDynamicAopProxy的invoke拦截方法:

关注重点3:在获取MethodInterceptor链后,如果为空,则没有拦截器直接执行目标对象的方法。retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);中的target对于本工程来说就是AServiceImpl,所以此方法的本质就是利用反射执行AServiceImpl的method方法。如下:

public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
			throws Throwable {

		// Use reflection to invoke the method.
		try {
			ReflectionUtils.makeAccessible(method);
			return method.invoke(target, args);
		}
           //略
	
	}

关注的重点4: 有了拦截器链后,就构造一个ReflectiveMethodInvocation来完成这一个调用过程。
首先说下接口情况:ReflectiveMethodInvocation实现了ProxyMethodInvocation,ProxyMethodInvocation继承了MethodInvocation,
MethodInvocation继承了Invocation,
Invocation继承了Joinpoint,此时的Joinpoint是AOP联盟定义的接口。
aspectj也有一个类似的JoinPoint,这两个是不一样的。
Joinpoint:能够得到目标对象,同时指定了对目标对象的处理方法

//定义了一个proceed()方法,来处理这些拦截器链的调用过程
 Object proceed() throws Throwable;
//返回目标对象,针对本工程就是AServiceImpl
   Object getThis();

Invocation:能够获取方法参数,此时方法可以是构造方法也可以是一般方法
Object[] getArguments();

MethodInvocation:能够获取方法而不是构造方法,ConstructorInvocation才是获取构造方法,所以有了MethodInvocation就可以执行目标对象的方法了。
Method getMethod();

ProxyMethodInvocation:能够获取代理代理对象,这个在Around通知时发挥作用,稍后会看到。
Object getProxy();

此时ReflectiveMethodInvocation就拥有以下数据:
protected final Object proxy;
	protected final Object target;
	protected final Method method;
	protected Object[] arguments;
	private final Class<?> targetClass;
        protected final List<?> interceptorsAndDynamicMethodMatchers;


interceptorsAndDynamicMethodMatchers则是由重点2中得出的拦截器链传给ReflectiveMethodInvocation的。然后看下ReflectiveMethodInvocation作为一个Joinpoint的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);
		}
	}

首先就是this.currentInterceptorIndex,它是ReflectiveMethodInvocation的一个属性,从-1开始:
private int currentInterceptorIndex = -1;

当currentInterceptorIndex达到this.interceptorsAndDynamicMethodMatchers.size() - 1时,拦截器链执行完毕了,就去执行目标对象的方法。invokeJoinpoint()方法就是上文我们所说的通过反射进行目标方法的调用。

继续看,拿出一个interceptorOrInterceptionAdvice,判断它是不是InterceptorAndDynamicMethodMatcher类型,这个类型在获取拦截器链的时候遇见了,我们再次回顾下:

for (Advisor advisor : config.getAdvisors()) {
			if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
					MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
//重点在这里重点在这里重点在这里重点在这里重点在这里
					if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {
						if (mm.isRuntime()) {
							// Creating a new object instance in the getInterceptors() method
							// isn't a problem as we normally cache created chains.
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				}
			}
//略

因为InterceptorAndDynamicMethodMatcher的MethodMatcher是可变的,所以在执行前仍要进行判断一次,符合的话就执行InterceptorAndDynamicMethodMatcher中所包含的MethodInterceptor。不符合的话跳过本次拦截器,继续执行下一个拦截器。

当拦截器是MethodInterceptor,则是执行这个拦截器。
然后我们来看下具体有哪些拦截器链,以及具体是怎样的执行过程:

我们会看到会有如下5个拦截器,依次是:
ExposeInvocationInterceptor、MethodBeforeAdviceInterceptor、AspectJAfterAdvice、AspectJAroundAdvice、AspectJAfterThrowingAdvice。

首先看第一个ExposeInvocationInterceptor:
它是Spring自动帮我们添加的,它不属于InterceptorAndDynamicMethodMatcher类型,即不再进行判断是否符合当前函数。
看下它的invoke方法的内容:

public Object invoke(MethodInvocation mi) throws Throwable {
		MethodInvocation oldInvocation = invocation.get();
		invocation.set(mi);
		try {
			return mi.proceed();
		}
		finally {
			invocation.set(oldInvocation);
		}
	}

其中MethodInvocation mi始终是刚创建的ReflectiveMethodInvocation对象,它包含了一切要执行的信息。invocation则是一个ThreadLocal类型,如下:
private static final ThreadLocal<MethodInvocation> invocation =
			new NamedThreadLocal<MethodInvocation>("Current AOP method invocation");

这里使用了ThreadLocal模式,将ReflectiveMethodInvocation对象这一重要对象存进当前线程的map集合中(ThreadLocal模式参见 http://lgbolgger.iteye.com/blog/2117216),实现线程内ReflectiveMethodInvocation对象这一重要数据的共享,这就意味着我们在自定义的advice中可以通过invocation来获取ReflectiveMethodInvocation对象这一重要数据。
同时ExposeInvocationInterceptor的currentInvocation方法用于获取当前线程的ReflectiveMethodInvocation对象

public static MethodInvocation currentInvocation() throws IllegalStateException {
		MethodInvocation mi = invocation.get();
		if (mi == null)
			throw new IllegalStateException(
					"No MethodInvocation found: Check that an AOP invocation is in progress, and that the " +
					"ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that " +
					"advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor!");
		return mi;
	}

ExposeInvocationInterceptor即分析完了,此时ExposeInvocationInterceptor的invoke函数还没有执行完毕,然后就嵌套执行ReflectiveMethodInvocation对象的proceed方法,
获取下一个拦截器MethodBeforeAdviceInterceptor,并且判断类型也属于InterceptorAndDynamicMethodMatcher类型的(我们自定义的几个都不属于),
和ExposeInvocationInterceptor一样,直接执行,看下MethodBeforeAdviceInterceptor的invoke函数:

public Object invoke(MethodInvocation mi) throws Throwable {
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
		return mi.proceed();
	}

先会执行我们在xml文件中配置好的advice,即我们自定义的doBefore方法,这就是前置通知。然后继续嵌套执行ReflectiveMethodInvocation对象的proceed方法,轮到了AspectJAfterAdvice的invoke方法:
@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		finally {
			invokeAdviceMethod(getJoinPointMatch(), null, null);
		}
	}

它的执行顺序则是先去执行后面的内容,当都执行完毕了才返回来执行这个advice。这就是后置通知。继续嵌套执行ReflectiveMethodInvocation对象的proceed方法,来到了AspectJAroundAdvice的invoke方法:
public Object invoke(MethodInvocation mi) throws Throwable {
		if (!(mi instanceof ProxyMethodInvocation)) {
			throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
		}
		ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
		ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
		JoinPointMatch jpm = getJoinPointMatch(pmi);
		return invokeAdviceMethod(pjp, jpm, null, null);
	}

protected ProceedingJoinPoint lazyGetProceedingJoinPoint(ProxyMethodInvocation rmi) {
		return new MethodInvocationProceedingJoinPoint(rmi);
	}

lazyGetProceedingJoinPoint(pmi)就是将ReflectiveMethodInvocation对象作为一个ProxyMethodInvocation封装成一个MethodInvocationProceedingJoinPoint,如下:
public MethodInvocationProceedingJoinPoint(ProxyMethodInvocation methodInvocation) {
		Assert.notNull(methodInvocation, "MethodInvocation must not be null");
		this.methodInvocation = methodInvocation;
	}

这个就是环绕通知,我们看下这通知我们所写的具体内容:
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {  
        long time = System.currentTimeMillis();  
        Object retVal = pjp.proceed();  
        time = System.currentTimeMillis() - time;  
        System.out.println("process time: " + time + " ms");  
        return retVal;  
    }

这里面有一个很重要的信息ProceedingJoinPoint pjp,它决定了ReflectiveMethodInvocation的执行链是否继续执行下去,所以pjp.proceed()的本质仍然是调用ReflectiveMethodInvocation的proceed()方法,来继续下面拦截器的执行,也可以选择不执行,则拦截器链的执行就会终止了,会从不断嵌套的proceed函数中不断返回。
这里将会执行到我们自定义的上面的doAround方法,当执行到pjp.proceed()时,又会返还到
ReflectiveMethodInvocation的proceed()执行下一个拦截器,来到AspectJAfterThrowingAdvice,它的invoke方法为:

public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		catch (Throwable t) {
			if (shouldInvokeOnThrowing(t)) {
				invokeAdviceMethod(getJoinPointMatch(), null, t);
			}
			throw t;
		}
	}

即先执行后面的拦截器,但后面的拦截器执行过程中出现异常时才会发挥该拦截器的作用。继续执行后面的拦截器,发现已经没了,则终于轮到目标对象的方法了,目标方法执行完毕后,返回上一个proceed的嵌套即AspectJAfterThrowingAdvice的invoke方法,发现没有抛出异常,则继续返回到上一个proceed嵌套,即AspectJAroundAdvice,即我们自定义的doAround中这一行代码Object retVal = pjp.proceed()返回了,继续完成我们自定义的doAround函数,完成后再返回上一个proceed嵌套,来到AspectJAfterAdvice,则开始执行这个advice的处理工作,即我们自定义的doAfter方法。再返回上一个proceed嵌套,来到MethodBeforeAdviceInterceptor,发现已经执行完毕继续返回上一个嵌套来到ExposeInvocationInterceptor,继续完成余下的工作,至此整个拦截过程就分析完毕了。在此过程中一个重要的参数就是我们配置的拦截器的顺序,顺序不同时执行过程就不一样,我们可以通过在xml配置中指定,下面附上我画的拦截器链的执行流程图。










  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
粉丝 927
博文 104
码字总数 265075
×
乒乓狂魔
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: