本篇文章從Aop xml元素的解析開始,分析了Aop在Spring中所使用到的技術。包括Aop各元素在容器中的表示方式、Aop自動代理的技術、代理對象的生成及Aop攔截鏈的調用等等。將這些技術串聯起來,就能勾勒出Aop在Spring中的使用脈絡。
一、Spring Aop的解析
在Spring xml配置中,不同的功能配置通過不同的命名空間引入,如事務方面的配置通過引入http://www.springframework.org/schema/tx命名空間來實現,而對Aop的配置則是引入http://www.springframework.org/schema/aop。對於引入的這些命名空間及其元素,Spring注冊了不同的NamespaceHandler類型來處理,如Aop配置元素的解析就是通過AopNamespaceHandler來實現。在Spring中,各種NamespaceHandler的繼承關系如下圖所示:
以Aop配置信息為例,對於該命名空間各元素的解析主要是通過AopNamespaceHandler來注冊,每一個元素對應一個解析器,其源碼如下所示:
public void init() { // In 2.0 XSD as well as in 2.1 XSD. registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser()); registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser()); registerBeanDefinitionDecorator("scoped-proxy", new copedProxyBeanDefinitionDecorator()); // Only in 2.0 XSD: moved to context namespace as of 2.1 registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); }
如果在xml配置文件中配置了<aop:aspectj-autoproxy />元素,則是通過SpringConfiguredBeanDefinitionParser類來解析。Spring將所要用到的handler分別配置在jar包的META-INF/ spring.handlers文件中,spring-aop包中的文件信息如下:
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
對spring.handlers文件的加載則是在DefaultNamespaceHandlerResolver中,
private Map<String, Object> getHandlerMappings() { if (this.handlerMappings == null) { synchronized (this) { if (this.handlerMappings == null) { try { Properties mappings =PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size()); CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); his.handlerMappings = handlerMappings; } catch (IOException ex) { } } } } return this.handlerMappings; }
其中handlerMappingsLocation為META-INF/spring.handlers。DefaultNamespaceHandlerResolver類存放在XmlReaderContext類中,由BeanDefinitionParserDelegate和BeanDefinitionParserDelegate調用,而這兩個則在DefaultBeanDefinitionDocumentReader類中使用,最終由各種BeanFarctory(Spring容器)來調用。
二、注冊AutoProxyCreator類
針對@AspectJ風格的AOP,Spring Aop提供了兩個AutoProxyCreator實現類進行自動代理,分別是AspectJAwareAdvisorAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator,對應於配置文件(<aop:config>)和使用注解的方式。在配置文件中加入<aop:aspect-autoproxy/>,spring會使用AnnotationAwareAspectJAutoProxyCreator,而加入<aop:config />則會使用AspectJAwareAdvisorAutoProxyCreator。在Spring內部,只會使用其中之一。如果兩種方式都配置了,則會使用AnnotationAwareAspectJAutoProxyCreator(在spring內部注解方式的優先級更高),詳情可以查看AopConfigUtils類。因為AnnotationAwareAspectJAutoProxyCreator繼承於AspectJAwareAdvisorAutoProxyCreator ,在調用自己的處理邏輯之前,會調用父類的實現邏輯,所以前者兼容后者。
三、Aop代理對象的生成
通過配置文件(或注解)將Aop切面配置好之后,ConfigBeanDefinitionParser類會將pointcut,advisor和aspect解析生成BeanDefinition,並注冊到相應的BeanFactory中,以便在AspectJAwareAdvisorAutoProxyCreator中使用,解析的代碼如下:
public BeanDefinition parse(Element element, ParserContext parserContext) { CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element)); parserContext.pushContainingComponent(compositeDef); configureAutoProxyCreator(parserContext, element); List<Element> childElts = DomUtils.getChildElements(element); for (Element elt: childElts) { String localName = parserContext.getDelegate().getLocalName(elt); if (POINTCUT.equals(localName)) { parsePointcut(elt, parserContext); } else if (ADVISOR.equals(localName)) { parseAdvisor(elt, parserContext); } else if (ASPECT.equals(localName)) { parseAspect(elt, parserContext); } } parserContext.popAndRegisterContainingComponent(); return null; }
每當讀取到aop:config元素時,spring會將其子元素分別解析並注冊到BeanFactory中。當調用getBean()時,BeanFactory會調用注冊到容器中的BeanPostProcessor(AspectJAwareAdvisorAutoProxyCreator)對象,判斷是否滿足攔截請求,如果滿足,則獲取所有滿足條件的Advisor,加入到ProxyFactory中,生成代理對象返回,其代碼如下所示:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { // Create proxy if we have advice. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); Object proxy = createProxy(bean.getClass(), beanName, pecificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
在BeanFactory生成Bean對象的時候,會對Bean對象進行一些初始化操作,1)判斷是否繼承aware接口,然后注入相應的aware對象;2)調用BeanPostProcessor類中的postProcessBeforeInitialization方法;3)判斷是否繼承InitializingBean,然后afterPropertiesSet方法;4),調用BeanPostProcessor類中的postProcessAfterInitialization方法。對代理對象的生成主要是在第2和第4步,其代碼如下所示:
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { invokeAwareMethods(beanName, bean); return null; } }, getAccessControlContext()); } else { // step 1 invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { // step 2 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { // step 3 invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { // step 4 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
在Spring容器中有可能會注冊多個BeanPostProcessor,執行第2和第4步時,它會返回第一個不為空的對象,這時起作用的只有一個BeanPostProcessor對象,所以在注冊自動代理對象的時候要尤為注意,其代碼如下所示:
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { result = beanProcessor.postProcessAfterInitialization(result, beanName); if (result == null) { return result; } } return result; }
四、Aop攔截鏈的調用
在代理對象中,有可能會有多個Advisor對象與之匹配,這些Advisor會在最終目標對象執行之前,按照指定的順序和優先級執行,順序號決定優先級,順序號越小,優先級越高,優先級排在前面的,將被優先執行。默認情況下,如果不明確指定各個Advisor的執行順序,那么Spring會按照它們的聲明順序應用他們,最先聲明的順序號最小但優先級最大,其次將之。順序號由Ordered指定,在BeanPostProcessor 各個子類中實現排序,如AspectJAwareAdvisorAutoProxyCreator中的sortAdvisors函數。
代理對象中會存有一個Advisor列表,以JdkDynamicAopProxy(CglibAopProxy類似)為例,它實現了InvocationHandler接口,並將自己傳給了代理對象,在代理對象中會調用其invoke方法,在該方法中有一段關鍵代碼:
// 得到這個方法的攔截器鏈 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // 判斷攔截器鏈是否為空,若為空,直接調用目標方法 if (chain.isEmpty()) { // 直接調用目標方法 retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args); } else { // 調用攔截器鏈 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); retVal = invocation.proceed(); }
調用攔截器鏈的功能主要ReflectiveMethodInvocation類中的proceed方法來實現,代碼如下所示:
public Object proceed() throws Throwable { // 判斷是否到了鏈尾,如果是則執行目標方法,調用結束。 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } // 取出當前的Advisor; Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // 判斷是否需要動態判斷參數,如果需要則執行如下操作; // 參數驗證通過則執行Advisor,否則跳過該Advisor; InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { return proceed(); } } else { // 如果不需要動態判斷參數,則執行該Advisor,因為在之前已經驗證通過了; return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
在上面的代碼中,好像並沒有類似遞歸操作的語句(也沒有循環語句)來執行攔截器鏈,那么代碼是怎么執行多個Advisor的?Spring對三種Advisor(MethodBeforeAdvice,AfterReturningAdvice和ThrowsAdvice)采用了適配器方式,將它們轉換為MethodInterceptor方法,如MethodBeforeAdviceAdapter實現如下:
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable { public boolean supportsAdvice(Advice advice) { return (advice instanceof MethodBeforeAdvice); } public MethodInterceptor getInterceptor(Advisor advisor) { MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice(); return new MethodBeforeAdviceInterceptor(advice); } }
MethodBeforeAdvice的語義是指在目標方法之前執行,在MethodBeforeAdviceInterceptor類按照其語義進行了轉義,使得在ReflectiveMethodInvocation類中可以統一用invoke方法進行調用,其invoke方法代碼如下所示:
public Object invoke(MethodInvocation mi) throws Throwable { this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() ); return mi.proceed(); }
對比BeforeAdvice的適配,現在應該可以想像AfterReturningAdvice的適配了,那就是先執行mi.proceed()方法,然后再執行advice的after方法,用代碼來驗證一下:
public Object invoke(MethodInvocation mi) throws Throwable { Object retVal = mi.proceed(); this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); return retVal; }
回到剛才的問題,代碼是怎么完成遞歸操作的?看了代碼,應該很清楚了。在MethodBeforeAdviceInterceptor的invoke方法中,有mi.proceed()這樣的語句,而mi則是由ReflectiveMethodInvocation傳入的this對象,即其自身對象。可以用如下的序列圖來表示:
五、總結
用一句話來形容Aop,那就是proxy+Interceptors+target,即一個代理對象,多個攔截器再加目標對象的技術。
附錄:
1、《spring揭穿》。
(轉載本站文章請注明作者和出處 http://www.cnblogs.com/noahsark/ ,請勿用於任何商業用途)