2019独角兽企业重金招聘Python工程师标准>>>

本篇文章将会介绍上一个例子中的源码执行情况,从中熟悉整个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 {@Autowiredprivate AService aService;@Autowiredprivate BServiceImpl bServiceImpl;@Testpublic 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中:

@Overridepublic 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;//我们关注的重点1if (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.
//关注的重点3retVal = 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 {@Overridepublic boolean supportsAdvice(Advice advice) {return (advice instanceof MethodBeforeAdvice);}@Overridepublic 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;
//返回目标对象,针对本工程就是AServiceImplObject 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方法:

@Overridepublic 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配置中指定,下面附上我画的拦截器链的执行流程图。

转载于:https://my.oschina.net/Sheamus/blog/393608

Spring AOP源码分析(四)Spring AOP的JDK动态代理相关推荐

  1. Spring Security源码分析四:Spring Social实现微信社交登录

    2019独角兽企业重金招聘Python工程师标准>>> 社交登录又称作社会化登录(Social Login),是指网站的用户可以使用腾讯QQ.人人网.开心网.新浪微博.搜狐微博.腾讯 ...

  2. Spring Security 源码分析:Spring Security 授权过程

    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring I ...

  3. Spring Ioc 源码分析(一)--Spring Ioc容器的加载

    1.目标:熟练使用spring,并分析其源码,了解其中的思想.这篇主要介绍spring ioc 容器的加载 2.前提条件:会使用debug 3.源码分析方法:Intellj idea debug 模式 ...

  4. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  5. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  6. Spring AOP 源码分析 - 筛选合适的通知器

    1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...

  7. spring AOP源码分析(一)

    spring AOP源码分析(一) 对于springAOP的源码分析,我打算分三部分来讲解:1.配置文件的解析,解析为BeanDefination和其他信息然后注册到BeanFactory中:2.为目 ...

  8. 一步一步手绘Spring AOP运行时序图(Spring AOP 源码分析)

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程) 一步一步手绘Spring IOC运行时序图二(基于XM ...

  9. 【Spring】Spring AOP源码分析-导读(一)

    文章目录 1.简介 2.AOP 原理 3.AOP 术语及相应的实现 3.1 连接点 - Joinpoint 3.2 切点 - Pointcut 3.3 通知 - Advice 3.4 切面 - Asp ...

  10. Spring 源码分析(四) ——MVC(二)概述

    随时随地技术实战干货,获取项目源码.学习资料,请关注源代码社区公众号(ydmsq666) from:Spring 源码分析(四) --MVC(二)概述 - 水门-kay的个人页面 - OSCHINA ...

最新文章

  1. python-docx官方声明
  2. 点击按钮取GridView当前被操作行的数据
  3. python处理excel教程实例-Python玩转Excel的读写改实例
  4. python引用类 魔法方法_Python 学习笔记 -- 类的魔法方法
  5. 基于结构化数据的文本生成:非严格对齐生成任务及动态轻量的GCN生成模型
  6. 详解GPU技术关键参数和应用场景
  7. 第11课 尼克与强盗 《小学生C++趣味编程》
  8. .NET牛人应该知道些什么(转)?
  9. mysql 8.0 postgresql_PostgreSQL8.0的安装和配置- -
  10. r语言clind函数_R 语言学习笔记 1
  11. SpringCloud工作笔记051---SpringCloud打包部署流程
  12. eclipse项目如何变成web项目_Eclipse中将Java项目转换成Web项目的方法
  13. 解决Pycharm添加虚拟解释器的报错问题
  14. 剪映专业版mac版本要求 剪映mac版最新版更新
  15. 秩和比综合评价法(RSR)详解及Python实现和应用
  16. gmx genion命令
  17. clk_get_rate函数
  18. 数据库字符集utf8和utf8mb4的详细区别
  19. qrcode将生成的二维码转成img格式
  20. 红米AirDots蓝牙耳机如何重置并进行双耳连接

热门文章

  1. death coming一直连接服务器,Death Coming总是显示无法连接网络
  2. java socket 握手_TCP建立连接三次握手过程详解(wireshark截图、java socket源码)
  3. 直方图均衡 视觉显著_计算机视觉一些项目实战技术(续)
  4. python字典嵌套列表怎么访问值的某个元素_python – 访问嵌套在字典中的值
  5. java 好用的 schedule_Java用Timer schedule搞定定时职务
  6. 树莓派3B+编译OpenCV3.4.3详细步骤
  7. [转]神奇选股指标问世,每月稳定获利有保障
  8. 这是人类高手输给AI的第一场辩论赛
  9. 活动推荐 | 首届“中新人工智能高峰论坛”,与周志华、李德毅等大咖对话未来...
  10. 活动合作 | 期待!GMIC北京2018即将拉开大幕