1. 程式人生 > >Spring 源碼分析(九)--AOP

Spring 源碼分析(九)--AOP

see nbsp 完成 owa new util 定義 sep ret

我們知道,使用面向對象編程(OOP)有一些弊端,當需要為多個不具有繼承關系的對象引入同一個公共行為時,例如日誌,安全檢測等,我們只有在每個對象裏引用公共行為,這樣程序中就產生了大量的重復代碼,程序就不便於維護了,所以就有了一個對面向對象編程的補充,即面向方面編程(AOP),AOP所關註的方向是橫向的,不同於OOP的縱向。

Spring 2.0 采用@AspectJ註解對POJO進行標註,從而定義一個包含切點信息和增強橫切邏輯的切面。Spring2.0可以將這個切面織入到匹配的目標Bean中。@AspectJ註解使用AspectJ切點表達式語法進行切點定義,可以通過切點函數,運算符,通配符等高級功能進行切點定義,擁有強大的連接點描述能力。

一:動態AOP使用示例

(1)創建用於攔截的bean

在實際工作中,此bean可能是滿足業務需要的核心邏輯,例如test方法可能會封裝這某個核心業務,但是,如果我們想在test前後加入日誌來跟蹤調試,如果直接修改源碼並不符合面向對象的設計方法,而且隨意修改原有代碼也會造成一定的風險,還好接下來的Spring幫我們做到了這一點。

public class TestBean {
    private String testStr = "testStr";

    public String getTestStr() {
        return testStr;
    }

    
public void setTestStr(String testStr) { this.testStr = testStr; } public void test(){ System.out.println("test"); } }

(2)創建Advisor。

Spring中摒棄了最原始的繁雜配置方式而采用@AspectJ註解對POJO進行標註,使AOP的工作大大簡化,例如,在AspectJTest類中,我們要做的就是在所有類的test方法執行前在控制臺中打印beforeTest,而在所有類的test方法執行後打印afterTest,同時又使用環繞的方式在所有類的方法執行前後再次分別打印before1和after1。

@Aspect
public class AspectJTest {

    @Pointcut("execution(* *.test(..))")
    public void test(){

    }

    @Before("test()")
    public void beforeTest(){
        System.out.println("beforeTest");
    }

    @After("test()")
    public void afterTest(){
        System.out.println("afterTest");
    }

    public Object arountTest(ProceedingJoinPoint p){
        System.out.println("before1");
        Object obj = null;
        try{
            obj = p.proceed();
        }catch (Throwable e){
            e.printStackTrace();
        }
        System.out.println("after1");
        return obj;
    }
}

(3)創建配置文件

盡管註解可以取代XML配置,但是本文還是通過XML配置來開啟AOP功能,做更詳細的講解,這樣有助於理解。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
    <bean id = "test" class="test.TestBean" ></bean>
    <bean class="test.AspectJTest" ></bean>
</beans>

(4)測試

 public static void main(String[] args){
        ApplicationContext bf = new ClassPathXmlApplicationContext("aspectTest.xml");
        TestBean bean = (TestBean) bf.getBean("test");
        bean.test();
    }

(5)結果

beforeTest

before1

test

afterTest

after1

Spring實現了對所有類的test方法進行增加,使輔助功能可以獨立於核心業務之外,方便與程序的擴展和解耦。

那麽,Spring究竟是如何實現AOP的呢?首先我們知道,Spring是否支持註解的AOP是由一個配置文件控制的,也就是<aop:aspectj--autoproxy />,當在配置文件中聲明了這句配置的時候,Spring就會支持註解的AOP,那麽我們的分析就從這句註解開始。

二:動態AOP自定義標簽

之前講過Spring中的自定義註解,如果聲明了自定義的註解,那麽就一定會在程序中的某個地方註冊了對應的解析器。通過搜索發現在AopNamespaceHander中對應著這樣一段函數:

public class AopNamespaceHandler extends NamespaceHandlerSupport {

    /**
     * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
     * ‘{@code config}‘, ‘{@code spring-configured}‘, ‘{@code aspectj-autoproxy}‘
     * and ‘{@code scoped-proxy}‘ tags.
     */
    @Override
    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 ScopedProxyBeanDefinitionDecorator());

        // Only in 2.0 XSD: moved to context namespace as of 2.1
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
    }

}

我們可以得知,在解析配置文件的時候,一旦遇到aspectj-autoproxy註解時就會使用解析器AspectJAutoProxyBeanDefinitionParser進行解析,下面看看AspectJAutoProxyBeanDefinitionParser的內部實行。

(2.1)註冊AnnotationAwareAspectJAutoProxyCreator

所有解析器,因為是對BeanDefinitionParser接口的統一實現,入口都是從parse函數開始的,AspectJAutoProxyBeanDefinitionParser的parse函數如下:

class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
        extendBeanDefinition(element, parserContext);
        return null;
    }

    private void extendBeanDefinition(Element element, ParserContext parserContext) {
        BeanDefinition beanDef =
                parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
        if (element.hasChildNodes()) {
            addIncludePatterns(element, parserContext, beanDef);
        }
    }

    private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) {
        ManagedList<TypedStringValue> includePatterns = new ManagedList<TypedStringValue>();
        NodeList childNodes = element.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node node = childNodes.item(i);
            if (node instanceof Element) {
                Element includeElement = (Element) node;
                TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));
                valueHolder.setSource(parserContext.extractSource(includeElement));
                includePatterns.add(valueHolder);
            }
        }
        if (!includePatterns.isEmpty()) {
            includePatterns.setSource(parserContext.extractSource(element));
            beanDef.getPropertyValues().add("includePatterns", includePatterns);
        }
    }

}

其中registerAspectJAnnotationAutoProxyCreatorIfNecessary 函數是我們比較關心的,也是關鍵邏輯的實現。

/**
* @author Rob Harrop
 * @author Juergen Hoeller
 * @author Mark Fisher
 * @since 2.0
 * @see AopConfigUtils
 */
public abstract class AopNamespaceUtils {

    /**
     * The {@code proxy-target-class} attribute as found on AOP-related XML tags.
     */
    public static final String PROXY_TARGET_CLASS_ATTRIBUTE = "proxy-target-class";

    /**
     * The {@code expose-proxy} attribute as found on AOP-related XML tags.
     */
    private static final String EXPOSE_PROXY_ATTRIBUTE = "expose-proxy";


    public static void registerAutoProxyCreatorIfNecessary(
            ParserContext parserContext, Element sourceElement) {

        BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(
                parserContext.getRegistry(), parserContext.extractSource(sourceElement));
        useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
        registerComponentIfNecessary(beanDefinition, parserContext);
    }

    public static void registerAspectJAutoProxyCreatorIfNecessary(
            ParserContext parserContext, Element sourceElement) {

        BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
                parserContext.getRegistry(), parserContext.extractSource(sourceElement));
        useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
        registerComponentIfNecessary(beanDefinition, parserContext);
    }

    public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
            ParserContext parserContext, Element sourceElement) {

        BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
                parserContext.getRegistry(), parserContext.extractSource(sourceElement));
//對於proxy-target-class以及expose--proxy屬性的處理 useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
//註冊組件並通知,便於監聽器做進一步的處理
//其中beanDefinition的className為AnnotationAwareAspectJAutoProxyCreator registerComponentIfNecessary(beanDefinition, parserContext); }
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) { if (sourceElement != null) { boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE)); if (proxyTargetClass) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE)); if (exposeProxy) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } private static void registerComponentIfNecessary(BeanDefinition beanDefinition, ParserContext parserContext) { if (beanDefinition != null) { BeanComponentDefinition componentDefinition = new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME); parserContext.registerComponent(componentDefinition); } } }

在registerAspectJAnnotationAutoProxyCreatorIfNecessary方法中主要完成了3件事情,基本上每行代碼就是一個完整的邏輯。

1. 註冊或升級AnnotationAwareAspectJAutoProxyCreator

對於AOP的實現,基本上都是靠AnnotationAwareAspectJAutoProxyCreator去完成,它可以根據@Point註解定義的切點來自動代理相匹配的bean。

Spring 源碼分析(九)--AOP