本文章着重分析AOP实现中的方法链调用。阅读本文章时,强烈建议打开IDE自己跟着方法调用一层层走。建议从ProxyFactoryBean类中的getObject()方法入手。
spring与AOP有关的模块有2个,一个是spring-aop, 这是spring自己的aop实现,另一个是spring-aspect,这是利用 aspectJ的aop实现。这两者最大的区别在于Spring中AOP是基于JDK动态代理,而AspectJ是基于字节码操作。这篇文章主要介绍基于JDK动态代理的实现。
先回顾一下代理模式。代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。代理对象可以在目标对象实现的基础上,增强额外的功能操作,扩展目标对象的功能。对比spring-aop, 这里要提到两个概念。第一个是Pointcut, Pointcut就是核心关注点,也就是代理模式中被代理的对象,第二个是advice, advice就是横切关注点,也就是切面,对应代理模式中对代理对象的增强功能。在spring中advice切面功能通过拦截器来实现。
AOP的实现主要分代理对象的生成与拦截器的调用两部分。
代理对象的生成 Spring有三个类负责代理对象的生成,ProxyFactoryBean、ProxyFactory和AspectJProxyFactory。三者都在同一集成体系下。其中ProxyFactoryBean和ProxyFactory使用的spring自己的实现,两者的区别在于ProxyFactoryBean可以直接通过声明式配置而ProxyFactory只能通过编程式配置。 AspectJProxyFactory利用的是AspectJ的实现。下面以ProxyFactoryBean为例详细讲讲Proxy对象的生成过程。
getObject() 首先ProxyFactoryBean它是一个FactoryBean。根据FactoryBean的特性可以知道Proxy对象生成的入口位于getObject方法。
1 2 3 4 5 6 7 8 9 10 11 12 public Object getObject () throws BeansException { this .initializeAdvisorChain(); if (this .isSingleton()) { return this .getSingletonInstance(); } else { if (this .targetName == null ) { this .logger.warn("Using non-singleton proxies with singleton targets is often undesirable. Enable prototype proxies by setting the 'targetName' property." ); } return this .newPrototypeInstance(); } }
可以看到getObject方法中首先通过initializeAdvisorChain对通知链进行了初始化。为什么是AdvisorChain而不是AdviceChain呢。这里就要提到第三个概念advisor。如果我们去看Advisor的默认实现,可以发现advisor持有一个Pointcut和一个advice对象。Advisor的作用就在于把这两个对象结合起来,告诉spring该使用哪个advice并在哪个Pointcut使用它。关于对通知链初始化的具体过程我们留在后面再说。
getSingletonInstance() 继续看getObject方法可以看到,spring在生成代理对象时作了对象类型的区分,两者差别不大。我们以getSingletonInstance方法为例,它干了两件事。第一是通过setInterfaces获取代理接口,第二是通过getProxy方法获取代理对象。setInterfaces不用说,就是代理模式代理对象需要实现被代理对象的接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private synchronized Object getSingletonInstance () { if (this .singletonInstance == null ) { this .targetSource = this .freshTargetSource(); if (this .autodetectInterfaces && this .getProxiedInterfaces().length == 0 && !this .isProxyTargetClass()) { Class<?> targetClass = this .getTargetClass(); if (targetClass == null ) { throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy" ); } this .setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this .proxyClassLoader)); } super .setFrozen(this .freezeProxy); this .singletonInstance = this .getProxy(this .createAopProxy()); } return this .singletonInstance; }
createAopProxy() 看getProxy方法。可以看到getProxy方法接受一个AopProxy参数,而这个参数是由createAopProxy方法获取的。再往下走可以发现spring使用AopProxyFactory来创建AopProxy,具体使用的对象是DefaultAopProxyFactory。所以我们去DefaultAopProxyFactory看一下AopProxy是如何被创建的。在createAopProxy方法中可以看到,默认生成JdkDynamicAopProxy,如果目标对象设置了targetClass并且targetClass不是一个接口类且不是代理类,则会生成 ObjenesisCglibAopProxy。无论生成哪一种AopProxy,都需要一个AdvisedSupport config作为参数,这个AdvisedSupport实际上就是对AopProxy所需参数的封装。
1 2 3 4 5 6 7 8 9 10 11 12 public AopProxy createAopProxy (AdvisedSupport config) throws AopConfigException { if (!config.isOptimize() && !config.isProxyTargetClass() && !this .hasNoUserSuppliedProxyInterfaces(config)) { return new JdkDynamicAopProxy(config); } else { Class<?> targetClass = config.getTargetClass(); if (targetClass == null ) { throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation." ); } else { return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config)); } } }
getProxy() 从JdkDynamicAopProxy的getProxy方法中可以看到,spring使用Proxy对象通过反射获取Proxy。Proxy的是JDK自带的类,在此就不多赘述了。有兴趣的同学还可以去CglibAopProxy中的getProxy方法中查看使用cglib生成AopProxy的过程。可以看到第一行声明的enhancer已经是cglib中的类了。
1 2 3 4 5 6 7 8 9 10 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, true ); this .findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this ); }
拦截器的调用 现在我们已经拥有了AopProxy对象了,在ProxyFactoryBean中配置的target已经不会被应用直接调用其方法实现了,而是会被AopProxy代理对象拦截。另外,相关的拦截器也通过被配置到代理对象中了。那么,这些拦截器是如何被调用的呢?
proceed() 对于JdkDynamicAopProxy,是通过实现InvocationHandler中的invoke方法来实现的。可以看到在invoke方法中,主要通过生成的ReflectiveMethodInvocation对象的proceed方法来遍历拦截器链。在CglibAopProxy中的实现类似,可以看到在intercept方法中同样生成了ReflectiveMethodInvocation的子对象,并调用其proceed方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 public Object proceed () throws Throwable { if (this .currentInterceptorIndex == this .interceptorsAndDynamicMethodMatchers.size() - 1 ) { return this .invokeJoinpoint(); } else { Object interceptorOrInterceptionAdvice = this .interceptorsAndDynamicMethodMatchers.get(++this .currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice; return dm.methodMatcher.matches(this .method, this .targetClass, this .arguments) ? dm.interceptor.invoke(this ) : this .proceed(); } else { return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this ); } } }
下面来看看proceed方法。这个方法比较短,我们可以仔细研究一下。第一个if代码块中有一个currentInterceptorIndex,指的是当前的拦截器在interceptorList里的下标。把这句话翻译一下就是说如果当前拦截器是interceptorList里最后一个拦截器,则直接执行被代理对象的方法。那么是不是最后一个拦截器就不执行呢?不是。这里有个坑,可以看到在调用拦截器的时候会先执行递增++this.currentInterceptorIndex,并且currentInterceptorIndex的初始值是-1。所以在if中判断的当前拦截器,实际执行的是下一个拦截器。所以把这句话重新翻译一下就是如果当前拦截器已迭代完毕,则直接执行被代理对象的方法。
再来看else。第一句话通过一个interceptorsAndDynamicMethodMatchers获取到一个Object。看到这里我一下懵了。这个interceptorsAndDynamicMethodMatchers是个啥,获取到的Object又是啥。这里不卖关子,interceptorsAndDynamicMethodMatchers实际就是一个包含所有拦截器的List, 这个Object自然就是获取到的当前拦截器了。
getInterceptorsAndDynamicInterceptionAdvice() 关于这个List的获取,我们可以往回看到JdkDynamicAopProxy中的invoke方法。该方法中通过advised.getInterceptorsAndDynamicInterceptionAdvice 获取了这个List。进入getInterceptorsAndDynamicInterceptionAdvice方法可以看到该方法实际上只是为这个List设置了缓存,实际List获取是通过advisorChainFactory完成的。在advisorChainFactory的默认实现类DefaultAdvisorChainFactory里我们可以看到,getInterceptorsAndDynamicInterceptionAdvice方法完成了interceptorList的构建工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public List<Object> getInterceptorsAndDynamicInterceptionAdvice (Advised config, Method method, Class<?> targetClass) { List<Object> interceptorList = new ArrayList(config.getAdvisors().length); Class<?> actualClass = targetClass != null ? targetClass : method.getDeclaringClass(); boolean hasIntroductions = hasMatchingIntroductions(config, actualClass); AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); Advisor[] var8 = config.getAdvisors(); int var9 = var8.length; for (int var10 = 0 ; var10 < var9; ++var10) { Advisor advisor = var8[var10]; MethodInterceptor[] interceptors; if (advisor instanceof PointcutAdvisor) { PointcutAdvisor pointcutAdvisor = (PointcutAdvisor)advisor; if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { interceptors = registry.getInterceptors(advisor); MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) { if (mm.isRuntime()) { MethodInterceptor[] var15 = interceptors; int var16 = interceptors.length; for (int var17 = 0 ; var17 < var16; ++var17) { MethodInterceptor interceptor = var15[var17]; 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(actualClass)) { interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } else { Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } return interceptorList; }
getIntercepters() 需要注意的是,interceptorList是通过遍历AdvisorChain完成的,可以看到interceptors = registry.getInterceptors(advisor)方法接收一个advisor参数,返回一个interceptor数组,这个过程称为拦截器的注册。可以看到这个注册过程是通过AdvisorAdapterRegistry对象完成的。继续往下找可以看到这个registry对象实际上是一个DefaultAdvisorAdapterRegistry的单例。可以看到,这个类持有一个adapters的List, 包括了全部3种通知类型。在getIntercepters方法中,可以看到对于每个advice, spring都回遍历这个List用以判断这个advice属于什么类型的advice, 从而注册不同的intercepter。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry , Serializable { private final List<AdvisorAdapter> adapters = new ArrayList(3 ); public DefaultAdvisorAdapterRegistry () { this .registerAdvisorAdapter(new MethodBeforeAdviceAdapter()); this .registerAdvisorAdapter(new AfterReturningAdviceAdapter()); this .registerAdvisorAdapter(new ThrowsAdviceAdapter()); } public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException { List<MethodInterceptor> interceptors = new ArrayList(3 ); Advice advice = advisor.getAdvice(); if (advice instanceof MethodInterceptor) { interceptors.add((MethodInterceptor)advice); } Iterator var4 = this .adapters.iterator(); while (var4.hasNext()) { AdvisorAdapter adapter = (AdvisorAdapter)var4.next(); if (adapter.supportsAdvice(advice)) { interceptors.add(adapter.getInterceptor(advisor)); } } if (interceptors.isEmpty()) { throw new UnknownAdviceTypeException(advisor.getAdvice()); } else { return (MethodInterceptor[])interceptors.toArray(new MethodInterceptor[interceptors.size()]); } }
invoke() 以MethodBeforeAdviceAdapter为例,它将advice封装成MethodBeforeAdviceInterceptor,在它的invoke回调方法中可以看到,先执行advice的before回调,然后出发proceed方法去执行下一个拦截器的迭代。根据这个逻辑,不用看源码也能知道,在AfterReturningAdviceAdapter的invoke方法中,首先执行proceed,然后执行advice的afterReturning回调,从而实现后置通知。ThrowsAdviceInterceptor则持有一个exceptionHandlerMap来应对不同的错误。
1 2 3 4 public Object invoke (MethodInvocation mi) throws Throwable { this .advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); return mi.proceed(); }
TargetSource spring-aop对目标的增强是通过拦截器来完成的,但有些时候需要对目标本身进行处理,这就要用到TargetSource接口了。
比较常用的TargetSource实现有HotSwappableTargetSource,它可以使用户以线程安全的形式切换目标对象,提供所谓的热交换功能。在需要切换目标对象时,调用其swap方法即可。常见的应用场景如切换数据库源,使用mysql做测试,上线时切换oracle。
AspectJ的AOP实现 以上就是spring-aop的实现。这里稍微提一下AspectJ,它的AOP实现是通过编织阶段(Weaving Phase),对目标Java类型的字节码进行操作,将需要的Advice逻辑给编织进去,形成新的字节码。因为JVM执行的都是Java源代码编译后得到的字节码,所以AspectJ相当于在这个过程中做了一点手脚,让Advice能够参与进来。
编织阶段可以有两个选择,分别是加载时编织(也可以成为运行时编织)和编译时编织:加载时编织(Load-Time Weaving)是在JVM加载类的时候完成的。而编译时编织(Compile-Time Weaving)则需要使用AspectJ的编译器来替换JDK的编译器。