天天看點

spring源碼:Aop标簽解析原理詳解

spring源碼:Aop标簽解析原理詳解

      • Aop使用示例
      • 實作原理
      • 小結

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

關于Spring Aop的實作,由于其是使用自定義标簽進行驅動的,因而讀者朋友如果對Spring如何實作自定義标簽比較熟悉,那麼可以繼續往下閱讀,否則可以閱讀完本文後再本人前面的文章Spring自定義标簽解析與實作。

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類如下:

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

這裡切面類和目标類都已經聲明完成,但如果不将其加入Spring容器中,其是不會工作的,加入容器的方式非常簡單,下面就是一種方式:

<?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/>标簽實作切面的自動裝配。下面使我們運作整個程式的驅動類:

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

執行結果如下:

before run.
Tidy is running.
after run.
           

可以看到,我們在驅動類中擷取的是Dog的執行個體,并且運作其run()方法,但是最終的運作結果中也運作了切面類中的環繞邏輯。

實作原理

根據前面對Spring自定義标簽使用的講解,我們知道這裡<aop:aspectj-autoproxy/>就是一個自定義标簽,并且該标簽會在相應jar包的META-INF目錄下有一個spring.handlers檔案,該檔案中聲明了解析該标簽的類。通過檢視該類我們得到如下配置:

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

這裡我們打開AopNamespaceHandler,其實作如下:

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);
    }
}
           

這裡可以看到AnnotationAwareAspectJAutoProxyCreator的BeanDefinition在第一步進行了注冊,然後讀取标簽中的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;
}
           

可以看到,在真正生成AnnotationAwareAspectJAutoProxyCreator的BeanDefinition的時候,首先會判斷是否已經生成過該bean,這裡不會将已經生成的bean進行覆寫;如果沒有生成該bean,則建立一個并進行注冊。這裡需要說明的是,Spring注冊該bean的時候使用的order是Ordered.HIGHEST_PRECEDENCE,這麼設定的原因在于Spring使用該bean進行切面邏輯的織入,因而這個bean必須在所有使用者自定義的bean執行個體化之前進行執行個體化,而使用者自定義的bean的執行個體化優先級是比較低的,這樣才能實作織入代理邏輯的功能。

小結

本文首先使用一個簡單的示例展示了Spring Aop的使用方式,然後對标簽中的<aop:aspectj-autoproxy/>解析過程進行了講解。可以看到,該标簽的解析過程最終是生成了一個

AnnotationAwareAspectJAutoProxyCreator

的BeanDefinition,關于Spring是如何使用該類實作代理的邏輯将在下一篇文章中進行講解。

繼續閱讀