Spring Aop技術原理分析


  本篇文章從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的繼承關系如下圖所示:

image

以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對象,即其自身對象。可以用如下的序列圖來表示:

image

五、總結

用一句話來形容Aop,那就是proxy+Interceptors+target,即一個代理對象,多個攔截器再加目標對象的技術。

附錄:

1、《spring揭穿》。

(轉載本站文章請注明作者和出處 http://www.cnblogs.com/noahsark/ ,請勿用於任何商業用途)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM