Spring Aop標籤解析原理詳解

       對於Spring Aop的實現,是很是複雜的,其實現過程主要包含xml標籤的解析,切面表達式的解析,判斷bean是否須要應用切面邏輯,以及使用Jdk代理或者是Cglib代理生成代理類。本文主要講解Xml標籤的解析的實現原理,在接下來幾篇文章中,會依次對Spring Aop剩餘的實現過程進行講解。java

       關於Spring Aop的實現,因爲其是使用自定義標籤進行驅動的,於是讀者朋友若是對Spring如何實現自定義標籤比較熟悉,那麼能夠繼續往下閱讀,不然能夠閱讀完本文後再本人前面的文章Spring自定義標籤解析與實現node

1. Aop使用示例

首先咱們聲明瞭一個切面類以下:正則表達式

@Aspect
public class DogAspect {
  @Around("execution(public void com.business.Dog.*(..))")
  public Object aspect(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("before run.");
    Object result = joinPoint.proceed();
    System.out.println("after run.");
    return result;
  }
}

       該切面類主要用於環繞com.business.Dog類中的public類型的,而且返回值是void的全部方法,下面咱們就在com.business包中聲明一個Dog類以下:spring

public class Dog {
  public void run() {
    System.out.println("Tidy is running.");
  }
}

       這裏切面類和目標類都已經聲明完成,但若是不將其加入Spring容器中,其是不會工做的,加入容器的方式很是簡單,下面就是一種方式:app

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="dog" class="com.business.Dog"/>

    <bean id="aspect" class="com.business.DogAspect"/>

    <aop:aspectj-autoproxy/>
</beans>

       這裏須要說明的是,將DogAspect聲明爲一個bean並不能使其工做,由於其也僅僅只是一個bean而已,要使其工做還須要使用上面的<aop:aspectj-autoproxy/>標籤實現切面的自動裝配。下面使咱們運行整個程序的驅動類:ide

public class DogApp {
  public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    Dog dog = context.getBean(Dog.class);
    dog.run();
  }
}

       執行結果以下:ui

before run.
Tidy is running.
after run.

       能夠看到,咱們在驅動類中獲取的是Dog的實例,而且運行其run()方法,可是最終的運行結果中也運行了切面類中的環繞邏輯。spa

2. 實現原理

       根據前面對Spring自定義標籤使用的講解,咱們知道這裏<aop:aspectj-autoproxy/>就是一個自定義標籤,而且該標籤會在相應jar包的META-INF目錄下有一個spring.handlers文件,該文件中聲明瞭解析該標籤的類。經過查看該類咱們獲得以下配置:.net

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

       這裏咱們打開AopNamespaceHandler,其實現以下:scala

public class AopNamespaceHandler extends NamespaceHandlerSupport {
	@Override
	public void init() {
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}
}

       能夠看到,咱們須要解析的標籤解析器在這個類中進行了註冊,即AspectJAutoProxyBeanDefinitionParser,打開這個類其主要實現以下:

class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {

    // 解析標籤的時候將會執行的方法
    @Override
    @Nullable
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        // 註冊一個類型爲AnnotationAwareAspectJAutoProxyCreator的bean到Spring容器中
        AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
        // 經過讀取配置文件對擴展相關屬性   
        extendBeanDefinition(element, parserContext);
        return null;
    }

    private void extendBeanDefinition(Element element, ParserContext parserContext) {
        // 獲取前面註冊的AnnotationAwareAspectJAutoProxyCreator對應的BeanDefinition
        BeanDefinition beanDef =
            parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
        // 解析當前標籤的子標籤
        if (element.hasChildNodes()) {
            addIncludePatterns(element, parserContext, beanDef);
        }
    }

    // 解析子標籤中的name屬性,其能夠有多個,這個name屬性最終會被添加到
    // AnnotationAwareAspectJAutoProxyCreator的includePatterns屬性中,
    // Spring在判斷一個類是否須要進行代理的時候會判斷當前bean的名稱是否與includePatterns中的
    // 正則表達式相匹配,若是不匹配,則不進行代理
    private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) {
        ManagedList<TypedStringValue> includePatterns = new ManagedList<>();
        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;
                // 解析子標籤中的name屬性
                TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));
                valueHolder.setSource(parserContext.extractSource(includeElement));
                includePatterns.add(valueHolder);
            }
        }

        // 將解析到的name屬性設置到AnnotationAwareAspectJAutoProxyCreator
        // 的includePatterns屬性中
        if (!includePatterns.isEmpty()) {
            includePatterns.setSource(parserContext.extractSource(element));
            beanDef.getPropertyValues().add("includePatterns", includePatterns);
        }
    }
}

       這裏咱們繼續看AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);方法,該方法的實現以下:

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			ParserContext parserContext, Element sourceElement) {
    // 註冊AnnotationAwareAspectJAutoProxyCreator的BeanDefinition
    BeanDefinition beanDefinition = 
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
	parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    // 解析標籤中的proxy-target-class和expose-proxy屬性值,
    // proxy-target-class主要控制是使用Jdk代理仍是Cglib代理實現,expose-proxy用於控制
    // 是否將生成的代理類的實例防護AopContext中,而且暴露給相關子類使用
	useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    // 將註冊的BeanDefinition封裝到BeanComponentDefinition中
	registerComponentIfNecessary(beanDefinition, parserContext);
}

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, 
       @Nullable Element sourceElement) {
    if (sourceElement != null) {
        // 解析標籤中的proxy-target-class屬性值
        boolean proxyTargetClass =
            Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
        if (proxyTargetClass) {
            // 將解析獲得的proxy-target-class屬性值設置到上面生成的
            // AnnotationAwareAspectJAutoProxyCreator的BeanDefinition的proxyTargetClass
            // 屬性中
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }
        // 解析標籤中的expose-proxy屬性值
        boolean exposeProxy = 
            Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
        if (exposeProxy) {
            // 將解析獲得的expose-proxy屬性值設置到
            // AnnotationAwareAspectJAutoProxyCreator的exposeProxy屬性中
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
        }
    }
}

private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, 
       ParserContext parserContext) {
    // 若是生成的AnnotationAwareAspectJAutoProxyCreator的BeanDefinition成功,則將其封裝到
    // BeanComponentDefinition中,而且將其添加到ParserContext中
    if (beanDefinition != null) {
        BeanComponentDefinition componentDefinition =
            new BeanComponentDefinition(beanDefinition, 
                AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
        parserContext.registerComponent(componentDefinition);
    }
}

       這裏能夠看到AnnotationAwareAspectJAutoProxyCreatorBeanDefinition在第一步進行了註冊,而後讀取標籤中的proxy-target-class和expose-proxy屬性,而且將屬性值設置到生成的BeanDefinition中。最後將生成的BeanDefinition註冊到ParserContext中。這裏咱們繼續看AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement))方法,其實現以下:

@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
       @Nullable Object source) {
    // 註冊AnnotationAwareAspectJAutoProxyCreator類型的BeanDefinition
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class,
       registry, source);
}

@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls,
       BeanDefinitionRegistry registry, @Nullable Object source) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

    // 若是已經註冊過AnnotationAwareAspectJAutoProxyCreator的Definition,若是其
    // 和當前將要註冊的BeanDefinition是同一個類型,則再也不註冊,若是不一樣,則判斷其優先級比
    // 當前將要註冊的BeanDefinition要高,則將其類名設置爲當前要註冊的BeanDefinition的名稱
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = 
            registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        return null;
    }

    // 若是不存在已經註冊的Aop的bean,則生成一個,而且設置其執行優先級爲最高優先級,而且標識
    // 該bean爲Spring的系統Bean,設置完以後則對該bean進行註冊
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}

       能夠看到,在真正生成AnnotationAwareAspectJAutoProxyCreatorBeanDefinition的時候,首先會判斷是否已經生成過該bean,這裏不會將已經生成的bean進行覆蓋;若是沒有生成該bean,則建立一個並進行註冊。這裏須要說明的是,Spring註冊該bean的時候使用的order是Ordered.HIGHEST_PRECEDENCE,這麼設置的緣由在於Spring使用該bean進行切面邏輯的織入,於是這個bean必須在全部用戶自定義的bean實例化以前進行實例化,而用戶自定義的bean的實例化優先級是比較低的,這樣才能實現織入代理邏輯的功能。

3. 小結

       本文首先使用一個簡單的示例展現了Spring Aop的使用方式,而後對標籤中的<aop:aspectj-autoproxy/>解析過程進行了講解。能夠看到,該標籤的解析過程最終是生成了一個AnnotationAwareAspectJAutoProxyCreatorBeanDefinition,關於Spring是如何使用該類實現代理的邏輯將在下一篇文章中進行講解。

相關文章
相關標籤/搜索