Spring AOP源碼學習:AOP 註解的解析

前言

上文介紹了 AOP 的基本概念,本文開始進入 AOP 的源碼解析,本文仍以 AspectJ 來進行介紹,首先是 AOP 註解的解析。

 

正文

當使用 <aop:aspectj-autoproxy /> 註解開啓 AOP 功能時。

Spring會從“META-INF/spring.handlers” 配置文件中拿到該註解對應的 NamespaceHandlerSupport:AopNamespaceHandler

在 AopNamespaceHandler 的 init 方法會給該註解註冊對應的解析器,aspectj-autoproxy 對應的解析器是:AspectJAutoProxyBeanDefinitionParser。

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

我們知道,當解析到 <aop:aspectj-autoproxy /> 註解時,會調用 AspectJAutoProxyBeanDefinitionParser 的 parse方法。

關於自定義註解的解析內容之前 IoC 的文章介紹過了,如果不瞭解的可以參考:Spring IoC:parseCustomElement 詳解

 

AspectJAutoProxyBeanDefinitionParser#parse

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 1.註冊AspectJAnnotationAutoProxyCreator
    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
    // 2.對於註解中子節點的處理
    extendBeanDefinition(element, parserContext);
    return null;
}

1.註冊 AspectJAnnotationAutoProxyCreator,見代碼塊1。

 

代碼塊1:AopNamespaceUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        ParserContext parserContext, Element sourceElement) {
    // 1.註冊AspectJAnnotationAutoProxyCreator
    BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
            parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    // 2.對於proxy-target-class以及expose-proxy屬性的處理
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    // 3.註冊組件並通知,便於監聽器做進一步處理
    registerComponentIfNecessary(beanDefinition, parserContext);
}

1.註冊 AspectJAnnotationAutoProxyCreator,見代碼塊2。

2.對於 proxy-target-class 以及 expose-proxy 屬性的處理,見代碼塊3。

 

代碼塊2:AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    // 1.如果註冊表中已經存在beanName=org.springframework.aop.config.internalAutoProxyCreator的bean,則按優先級進行選擇。
    // beanName=org.springframework.aop.config.internalAutoProxyCreator,可能存在的beanClass有三種,按優先級排序如下:
    // InfrastructureAdvisorAutoProxyCreator、AspectJAwareAdvisorAutoProxyCreator、AnnotationAwareAspectJAutoProxyCreator
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        // 拿到已經存在的bean定義
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        // 如果已經存在的bean的className與當前要註冊的bean的className不相同,則按優先級進行選擇
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            // 拿到已經存在的bean的優先級
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            // 拿到當前要註冊的bean的優先級
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                // 如果當前要註冊的bean的優先級大於已經存在的bean的優先級,則將bean的className替換爲當前要註冊的bean的className,
                apcDefinition.setBeanClassName(cls.getName());
            }
            // 如果小於,則不做處理
        }
        // 如果已經存在的bean的className與當前要註冊的bean的className相同,則無需進行任何處理
        return null;
    }
    // 2.如果註冊表中還不存在,則新建一個Bean定義,並添加到註冊表中
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    // 設置了order爲最高優先級
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // 註冊BeanDefinition,beanName爲org.springframework.aop.config.internalAutoProxyCreator
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}

org.springframework.aop.config.internalAutoProxyCreator 是內部管理的自動代理創建者的 bean 名稱,可能對應的 beanClassName 有三種,對應的註解如下:

InfrastructureAdvisorAutoProxyCreator:<tx:annotation-driven />

AspectJAwareAdvisorAutoProxyCreator:<aop:config />

AnnotationAwareAspectJAutoProxyCreator:<aop:aspectj-autoproxy />

當同時存在多個註解時,會使用優先級最高的 beanClassName 來作爲 org.springframework.aop.config.internalAutoProxyCreator 的 beanClassName。本系列文章暫不考慮同時存在其他註解的情況,所以在這邊會註冊的 beanClassName 爲:AnnotationAwareAspectJAutoProxyCreator。

 

代碼塊3:useClassProxyingIfNecessary

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
    if (sourceElement != null) {
        boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
        if (proxyTargetClass) {
            // 如果節點設置了proxy-target-class=true,則給beanName爲org.springframework.aop.config.internalAutoProxyCreator
            // 的BeanDefinition添加proxyTargetClass=true的屬性,之後創建代理的時候將強制使用Cglib代理
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }
        boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
        if (exposeProxy) {
            // 如果節點設置了expose-proxy=true,則給beanName爲org.springframework.aop.config.internalAutoProxyCreator
            // 的BeanDefinition添加exposeProxy=true的屬性,之後創建攔截器時會根據該屬性選擇是否暴露代理類
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
        }
    }
}

 

總結

本文的內容比較簡單,最重要的內容就是註冊了內部管理的自動代理創建者的 bean:AnnotationAwareAspectJAutoProxyCreator,AOP 的大部分重要內容都在這個bean 裏,之後會一一介紹。

 

相關文章

Spring AOP:基本概念

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章