博客 / 詳情

返回

聲明式事務與AOP

原始博文鏈接

出發點

閒來無事回顧幾個項目中的一些內容,把一些重複代碼用AOP重新處理了一下,定義切入點時採用了自定義註解的形式,原因是這樣最精準、最容易控制,當然缺點是需要手動把註解加到各個方法上去。項目裏還有用到聲明式事務(@Transactional)和聲明式緩存(@Cacheable),所以有的方法就會存在3個以上的切面相關注解,註解一多就發現對它們的執行順序機理的理解有些模糊,遂打算重新理一遍Spring AOP的內容,回顧一把這個經典到不能再經典的工具。為了方便,底子還是用的SpringBoot,順便也回顧下SpringBoot中事務、AOP等自動配置的相關內容,分析分析源碼。本文默認您已經有對Spring AOP與聲明式事務管理的基本認知,採用的版本信息:SpringBoot 2.3.3.RELEASE(Spring 5.2.8.RELEASE)

聲明式事務原理

首先整理下聲明式事務,在SpringBoot環境下不需要單獨引入事務相關的依賴或者單獨添加啓用事務的註解,通常引入相關的持久層依賴就可以直接使用@Transactional這個註解,而如果要使用Spring AOP您需要顯式引入依賴項spring-boot-starter-aop。聲明式事務本質上也是AOP思想的產物,那麼為什麼沒有諸如spring-boot-starter-transaction,為什麼使用聲明式事務不需要引入spring-aop是個值得思考的問題。

對於普通Spring項目來説,使用聲明式事務需要顯式進行指定。xml配置中需要添加標籤<tx:annotation-driven>(使@Transactional生效)或者使用標籤<tx:advice>來聲明事務切入;使用Java config形式則需要在配置類上添加註解@EnableTransactionManage。而SpringBoot可以不準確的描述為更高級的Java config,我們知道SpringBoot自帶的自動配置類都在spring-boot-autoconfigure包下,很容易找到事務相關的自動配置類org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration

@Configuration(proxyBeanMethods = false)
// 依賴PlatformTransactionManager, 該類位於spring-tx包下
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
        DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class })
// 引入事務屬性配置
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionAutoConfiguration {

    // 事務管理器自定義收集器
    @Bean
    @ConditionalOnMissingBean
    public TransactionManagerCustomizers platformTransactionManagerCustomizers(
            ObjectProvider<PlatformTransactionManagerCustomizer<?>> customizers) {
        return new TransactionManagerCustomizers(customizers.orderedStream().collect(Collectors.toList()));
    }

    // reactive相關,可以暫不考慮
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnSingleCandidate(ReactiveTransactionManager.class)
    public TransactionalOperator transactionalOperator(ReactiveTransactionManager transactionManager) {
        return TransactionalOperator.create(transactionManager);
    }

    // 配置TransactionTemplate
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnSingleCandidate(PlatformTransactionManager.class)
    public static class TransactionTemplateConfiguration {

        @Bean
        @ConditionalOnMissingBean(TransactionOperations.class)
        public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
            return new TransactionTemplate(transactionManager);
        }

    }

    // 事務的主要配置內容,根據使用的代理方式分成兩種:jdk proxy和cglib proxy
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnBean(TransactionManager.class)
    // 這個contion意味着你可以主動使用@EnableTransactional,不採用如下的默認配置
    @ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
    public static class EnableTransactionManagementConfiguration {

        @Configuration(proxyBeanMethods = false)
        // 使用與普通java config相同的註解
        @EnableTransactionManagement(proxyTargetClass = false)
        @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
                matchIfMissing = false)
        public static class JdkDynamicAutoProxyConfiguration {

        }

        @Configuration(proxyBeanMethods = false)
        // 同上, 只是註解的參數不同
        @EnableTransactionManagement(proxyTargetClass = true)
        @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
                matchIfMissing = true)
        public static class CglibAutoProxyConfiguration {

        }

    }

}

因此還是回到了@EnableTransactionManagement這注解上,值得額外注意的一點是當前版本SpringBoot環境下該註解的proxyTargetClass屬性默認設置為true(matchIfMissing的作用),這與註解屬性本身的默認值(false)不同,也就是説SpringBoot默認採用CGLIB作為動態代理解決方案,有關於這一點的具體可以參考這個issue的討論。接下來繼續深入該註解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

    boolean proxyTargetClass() default false;

    AdviceMode mode() default AdviceMode.PROXY;

    int order() default Ordered.LOWEST_PRECEDENCE;

}

該註解主要的功能就是引入TransactionManagementConfigurationSelector,其作為接口ImportSelector的實現類目的是引入一些Configuration配置類:

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

    // 根據@EnableTransactionManagement註解的mode屬性來確定引入的配置類
    @Override
    protected String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:
                // 默認使用PROXY,引入了兩個類
                return new String[] {AutoProxyRegistrar.class.getName(),
                        ProxyTransactionManagementConfiguration.class.getName()};
            case ASPECTJ:
                return new String[] {determineTransactionAspectClass()};
            default:
                return null;
        }
    }

    private String determineTransactionAspectClass() {
        return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
                TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
                TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
    }

}

以默認的PROXY模式為例(ASPECTJ需要額外引入spring-aspects依賴並執行其他操作),可以看到引入了兩個配置類:AutoProxyRegistrarProxyTransactionManagementConfiguration,這兩個類自身代碼量都不多,逐個查看源碼:

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    private final Log logger = LogFactory.getLog(getClass());

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean candidateFound = false;
        Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
        for (String annType : annTypes) {
            AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
            if (candidate == null) {
                continue;
            }
            Object mode = candidate.get("mode");
            Object proxyTargetClass = candidate.get("proxyTargetClass");
            if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
                    Boolean.class == proxyTargetClass.getClass()) {
                candidateFound = true;
                if (mode == AdviceMode.PROXY) {
                    // 重點在於如下幾句代碼,嘗試註冊AutoProxyCreator
                    AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                    if ((Boolean) proxyTargetClass) {
                        AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                        return;
                    }
                }
            }
        }
        if (!candidateFound && logger.isInfoEnabled()) {
            String name = getClass().getSimpleName();
            logger.info(String.format("%s was imported but no annotations were found " +
                    "having both 'mode' and 'proxyTargetClass' attributes of type " +
                    "AdviceMode and boolean respectively. This means that auto proxy " +
                    "creator registration and configuration may not have occurred as " +
                    "intended, and components may not be proxied as expected. Check to " +
                    "ensure that %s has been @Import'ed on the same class where these " +
                    "annotations are declared; otherwise remove the import of %s " +
                    "altogether.", name, name, name));
        }
    }

}

// ===========================================================

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

    // 事務Advisor實現類,引用了後面兩個Bean
    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
            TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {

        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        advisor.setTransactionAttributeSource(transactionAttributeSource);
        advisor.setAdvice(transactionInterceptor);
        if (this.enableTx != null) {
            advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
        }
        return advisor;
    }

    // 事務屬性解析器,使用的是AnnotaionTransactionAtributeSource,用於解析@Transactional註解的屬性
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionAttributeSource transactionAttributeSource() {
        return new AnnotationTransactionAttributeSource();
    }

    // 事務攔截器,事務處理的真正邏輯就在這個類中,如果想探究事務是如何執行的可以深入查看這個類的源碼
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
        TransactionInterceptor interceptor = new TransactionInterceptor();
        interceptor.setTransactionAttributeSource(transactionAttributeSource);
        if (this.txManager != null) {
            interceptor.setTransactionManager(this.txManager);
        }
        return interceptor;
    }

}

ProxyTransactionManagementConfiguration引入了事務處理的解析、處理和整體描述相關的Bean,那麼如何讓這些Bean發揮作用,真正應用到方法中就應該是AutoProxyRegistrar所引入的Bean(嚴格來説是Bean Definition)要做的事情。沿着關鍵代碼AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)的調用鏈一直到最後,可以看到如下代碼:

// from org.springframework.aop.config.AopConfigUtils

// 入參 cls 的類對象為InfrastructureAdvisorAutoProxyCreator
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
        Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

    // 查看是否存在固定名稱的BeanDefinition
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        // 如果這個名稱的Bean的對象類型與入參不同,則根據優先級來判斷是否替換對象類型
        // 查看findPriorityForClass方法,可以看到返回的是一個List的下標,這個list的
        // 內容是靜態寫死的,包含了三種類型(見下方代碼)。所以可以理解為這是一個逐步升級
        // 的邏輯,後面將進一步分析這三個類型
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        return null;
    }

    // 不存在則創建BD並註冊到BeanFactory
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    // 注意這個Bean的優先級被設為最高
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}

/**
 * Stores the auto proxy creator classes in escalation order.
 */
private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<>(3);
static {
    // Set up the escalation list...
    APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
    APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
    APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
}

在沒有其他配置的情況下InfrastructureAdvisorAutoProxyCreator將作為AutoProxyRegistrar引入的Bean的具體類型,查看它的類圖如下:

實現的核心接口為SmartInstaniationAwareBeanPostProcessor,它的繼承結構説明了它是一個修改Bean的鈎子,核心的處理邏輯方法是postProcessAfterInitialization

// from org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator

/**
 * Create a proxy with the configured interceptors if the bean is
 * identified as one to proxy by the subclass.
 * @see #getAdvicesAndAdvisorsForBean
 */
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            // 判斷是否需要使用代理來包裝
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
    // 這一步將從獲取BeanFactory中獲取匹配的Advisor,包括上述的事務Advisor
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 創建代理
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

看到這裏基本上就能夠明白註解配置的聲明式事務是如何生效的了,以PROXY模式為例,總結如下:

  1. 使用註解@EnableTransactionManagement,該註解根據配置的mode屬性引入了AutoProxyRegistrarProxyTransactionManagementConfiguration兩個組件。
  2. AutoProxyRegistrar嘗試向容器中添加自動代理生成器(Auto Proxy Creator)的BeanDefinition,採用的實現類為InfrastructureAdvisorAutoProxyCreator。APC實現了BeanPostProcessor接口,將在Bean實例化後執行代理的生成操作,PC會嘗試從BeanFactory中獲取實現了Advisor接口的Bean來作為代理生成的依據。
  3. ProxyTransactionManagementConfiguration是一個配置類,它註冊了BeanFactoryTransactionAttributeSourceAdvisor(事務增強Advisor)、TransactionAttributeSource(事務註解屬性解析器)、TransactionInterceptor(事務執行攔截器)三個組件。
  4. TransactionAttributeSource的職能是解析@Transactional註解中的屬性值幷包裝為事務相關屬性,TransactionInterceptor包含了事務的具體執行邏輯。這兩個Bean作為BeanFactoryTransactionAttributeSourceAdvisor的組件由它進行方法調用。
  5. APC將在Bean實例化時根據獲取到的事務Advisor判斷是否需要生成增強代理,如果Bean的類或方法上包含了@Transactional註解,那麼將生成包含事務攔截器的代理類,完成事務增強。

Spring AOP原理

其實我們都知道聲明式事務是AOP的子集,是它的應用之一,但是我們使用AOP需要引入spring-boot-starter-aop,而不引入並不影響我們使用聲明式事務。接下來一探究竟Spring AOP是如何生效的。

找到AOP的自動配置類org.springframework.boot.autoconfigure.aop.AopAutoConfiguration

@Configuration(proxyBeanMethods = false)
// 引入依賴後自動生效,可以通過配置spring.aop.auto=false來關閉
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

    // 依賴類org.aspectj.weaver.Advice,該類屬於包org.aspectj.weaver,並且
    // spring-aop依賴該包,因此通常這個配置會生效
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(Advice.class)
    static class AspectJAutoProxyingConfiguration {

        @Configuration(proxyBeanMethods = false)
        // 使用與普通java config相同的註解
        @EnableAspectJAutoProxy(proxyTargetClass = false)
        @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
                matchIfMissing = false)
        static class JdkDynamicAutoProxyConfiguration {

        }

        @Configuration(proxyBeanMethods = false)
        // 同上, 只是註解的參數不同
        @EnableAspectJAutoProxy(proxyTargetClass = true)
        @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
                matchIfMissing = true)
        static class CglibAutoProxyConfiguration {

        }

    }

    // 當org.aspectj.weaver.Advice不存在,即缺少依賴時作為候補配置項
    // 通常不會進入這個配置塊
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingClass("org.aspectj.weaver.Advice")
    @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
            matchIfMissing = true)
    static class ClassProxyingConfiguration {

        ClassProxyingConfiguration(BeanFactory beanFactory) {
            if (beanFactory instanceof BeanDefinitionRegistry) {
                BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
                AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
        }

    }

}

可以看到配置內容和聲明式事務非常相似,同樣還是以Java Config註解@EnableAspectJAutoProxy作為入口,也是將註解的proxy-target-class暴露到配置文件中。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

    // 是否採用CGLIB作為代理實現
    boolean proxyTargetClass() default false;

    // 是否將代理自身暴露到ThreadLocal環境以便在攔截器中能夠獲取到代理本身
    boolean exposeProxy() default false;

}

還是同樣的配方,使用了@Import註解引入組件,但是數量略有減少,只有一個名為AspectJAutoProxyRegistrar的工具類。

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    // 註冊Bean Definition
    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        // 註冊AutoProxyCreator,這裏使用實現類是AnnotationAwareAspectJAutoProxyCreator
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

        // 根據註解@EnableAspectJAutoProxy上的參數修改BD
        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }

}

很明顯起作用的還是添加Aspect Proxy Creator相關的Bean Definition,根據上述的優先級AnnotationAwareAspectJAutoProxyCreator將會覆蓋InfrastructureAdvisorAutoProxyCreator。這幾個候選APC的繼承關係如下圖所示:

對比一下InfrastructureAdvisorAutoProxyCreatorAspectJAwareAdvisorAutoProxyCreator這兩個同級的類分別覆蓋了哪些方法:

public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {

    @Nullable
    private ConfigurableListableBeanFactory beanFactory;

    @Override
    protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        super.initBeanFactory(beanFactory);
        this.beanFactory = beanFactory;
    }

    // 這個方法最終被org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans
    // 所使用,即限制只有框架本身的Advisor能夠生效
    @Override
    protected boolean isEligibleAdvisorBean(String beanName) {
        return (this.beanFactory != null && this.beanFactory.containsBeanDefinition(beanName) &&
                this.beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE);
    }

}

// ===========================================================

public class AspectJAwareAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {

    private static final Comparator<Advisor> DEFAULT_PRECEDENCE_COMPARATOR = new AspectJPrecedenceComparator();

    // 覆蓋了Advisor排序方法,本質原因是需要對源自統一個Aspect的Advisor進行進一步排序
    // 這排序還挺複雜的,應用了偏序排序,具體規則請查看該方法的javadoc
    @Override
    protected List<Advisor> sortAdvisors(List<Advisor> advisors) {
        List<PartiallyComparableAdvisorHolder> partiallyComparableAdvisors = new ArrayList<>(advisors.size());
        for (Advisor advisor : advisors) {
            partiallyComparableAdvisors.add(
                    new PartiallyComparableAdvisorHolder(advisor, DEFAULT_PRECEDENCE_COMPARATOR));
        }
        List<PartiallyComparableAdvisorHolder> sorted = PartialOrder.sort(partiallyComparableAdvisors);
        if (sorted != null) {
            List<Advisor> result = new ArrayList<>(advisors.size());
            for (PartiallyComparableAdvisorHolder pcAdvisor : sorted) {
                result.add(pcAdvisor.getAdvisor());
            }
            return result;
        }
        else {
            return super.sortAdvisors(advisors);
        }
    }

    // 添加一個暴露AspectJ調用(MethodInvocation)的前置Advisor,也是利用ThreadLocal
    @Override
    protected void extendAdvisors(List<Advisor> candidateAdvisors) {
        AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
    }

    // 作為Aspect本身的Bean不能被代理
    @Override
    protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        // TODO: Consider optimization by caching the list of the aspect names
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        for (Advisor advisor : candidateAdvisors) {
            if (advisor instanceof AspectJPointcutAdvisor &&
                    ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
                return true;
            }
        }
        return super.shouldSkip(beanClass, beanName);
    }

    // 用於排序的輔助類
    private static class PartiallyComparableAdvisorHolder implements PartialComparable {
        // ...
    }

}

可以看到兩者並沒有進行核心內容上的更改,都是針對各自應用場景的修補,InfrastructureAdvisorAutoProxyCreator只為框架本身的工具服務,而AspectJAwareAdvisorAutoProxyCreator添加了一些針對AspectJ聲明切面的處理邏輯,所以本質上兩者還是保持一致的。AspectJAwareAdvisorAutoProxyCreator並沒有創建Advisor的相關邏輯,而EnableAspectJAutoProxy只引入這一個工具,可以猜想到根據註解解析Advisor的內容包含在它的子類AnnotationAwareAspectJAutoProxyCreator中,忽略一些非主要代碼展示如下:

public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {

    @Nullable
    private List<Pattern> includePatterns;

    @Nullable
    private AspectJAdvisorFactory aspectJAdvisorFactory;

    @Nullable
    private BeanFactoryAspectJAdvisorsBuilder aspectJAdvisorsBuilder;

    // 忽略一些非主要代碼 ...

    @Override
    protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        super.initBeanFactory(beanFactory);
        // 創建advisor factory
        if (this.aspectJAdvisorFactory == null) {
            this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
        }
        // 創建advisor builder
        this.aspectJAdvisorsBuilder =
                new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
    }

    @Override
    protected List<Advisor> findCandidateAdvisors() {
        // 父類邏輯調用
        List<Advisor> advisors = super.findCandidateAdvisors();
        // 這裏將根據aspectj註解解析生成額外的advisor
        if (this.aspectJAdvisorsBuilder != null) {
            advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        }
        return advisors;
    }
    
    // 忽略一些非主要代碼 ...

}

正如這個類的名稱AnnotationAwareAspectJAutoProxyCreator,它添加了對aspectj註解(@Aspect@PointCut@Before 等等)的支持。Spring AOP生效邏輯總結如下:

  1. 使用註解@EnableAspectJAutoProxy,由它引入AspectJAutoProxyRegistrar,開啓對Java註解聲明式AOP的支持
  2. AspectJAutoProxyRegistrar向容器中添加AnnotationAwareAspectJAutoProxyCreator作為APC的實現類,它將覆蓋其他APC(如果存在)
  3. AnnotationAwareAspectJAutoProxyCreator除了對實現了Advisor接口的Bean的處理之外,同時會判斷Bean是否包含了aspectj相關的註解,並根據這些註解生成對應的Advisor
  4. APC將在Bean實例化時根據獲取到的Advisor判斷是否需要生成增強代理
  5. 三種候選APC優先級依次提高,分別對應不同的場景:

    1. InfrastructureAdvisorAutoProxyCreator:對應一些內部框架功能實現的支持,例如聲明式事務、聲明式緩存等
    2. AspectJAwareAdvisorAutoProxyCreator:對應XML配置場景下的AOP聲明支持(所以它不直接包含對於切面定義的處理)
    3. AnnotationAwareAspectJAutoProxyCreator:對應Java Config配置場景下註解式AOP聲明的支持

切入順序解析

當業務功能逐步增加,可能會加入各種各樣的切面邏輯,這種情況下就需要額外關注切入的順序,順序不對可能會造成嚴重的問題。接下來順着代碼看看Spring AOP對於切面順序是如何處理的。

經過上面的分析,我們可以從AbstractAutoProxyCreator的postProcessAfterInitialization方法進入:

// ========= AbstractAutoProxyCreator =========
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            // 進入這個方法內部
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
    // 很明顯這一步在獲取切面,返回的數組中內容的順序即代表了切面的順序
    // 這個方法在該類中為抽象方法,由子類覆蓋實現。由此處繼續往下走
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

// ========= AbstractAdvisorAutoProxyCreator =========

@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
        Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    // 繼續往下走
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    // 這裏對返回值做了一些處理
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY; // 如果沒有,返回null
    }
    return advisors.toArray();
}

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 這一步將獲取所有候選的Advisor
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 遍歷候選的Advisor篩選出其中與Bean相匹配的
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    // 本類中為空方法,AspectJAwareAdvisorAutoProxyCreator對其進行了覆蓋
    // 添加了一個暴露AOP調用的advisor,這個advisor的order值為PriorityOrdered.HIGHEST_PRECEDENCE + 1 
    extendAdvisors(eligibleAdvisors);
    // 對advisor進行排序,進入下面的方法,查看是如何排序的
    // 這一步不同的APC會有不同的實現,但是也是屬於擴展的關係
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

// AbstractAdvisorAutoProxyCreator的默認實現
protected List<Advisor> sortAdvisors(List<Advisor> advisors) {
    // 內部調用的是List.sort(AnnotationAwareOrderComparator.INSTANCE)
    AnnotationAwareOrderComparator.sort(advisors);
    return advisors;
}

// AspectJAwareAdvisorAutoProxyCreator的覆蓋實現
protected List<Advisor> sortAdvisors(List<Advisor> advisors) {
    List<PartiallyComparableAdvisorHolder> partiallyComparableAdvisors = new ArrayList<>(advisors.size());
    // 這裏把所有的advisor都包裝成為PartiallyComparableAdvisorHolder
    // DEFAULT_PRECEDENCE_COMPARATOR 的類型為AspectJPrecedenceComparator
    // 而這個類型內部同樣包含了一個AnnotationAwareOrderComparator
    for (Advisor advisor : advisors) {
        partiallyComparableAdvisors.add(
                new PartiallyComparableAdvisorHolder(advisor, DEFAULT_PRECEDENCE_COMPARATOR));
    }
    // 使用偏序排序,偏序排序屬於離散數學範疇,本人也不是特別清楚
    // 使用這種排序主要是為了處理同一個切面中聲明的advice
    List<PartiallyComparableAdvisorHolder> sorted = PartialOrder.sort(partiallyComparableAdvisors);
    if (sorted != null) {
        List<Advisor> result = new ArrayList<>(advisors.size());
        for (PartiallyComparableAdvisorHolder pcAdvisor : sorted) {
            result.add(pcAdvisor.getAdvisor());
        }
        return result;
    }
    else {
        return super.sortAdvisors(advisors);
    }
}

所以不同的切面之間都是使用AnnotationAwareOrderComparator來確定順序,該類繼承自OrderComparator,增加了對Spring註解@Order和J2EE註解@Priority的支持。它的比較的邏輯可以總結如下:

  1. 如果實現了Ordered接口則使用Ordered.getOrder()方法獲取排序值
  2. 如果沒有實現Ordered接口,查找類對象是否有添加兩種支持的註解(包括繼承關係),如果找到註解則使用註解的屬性值作為排序值
  3. 如果都沒有,排序值取Ordered.LOWEST_PRECEDENCE(2147483647)
  4. 如果兩者只有一方實現了PriorityOrdered接口,那麼實現方優先級更高,否則根據排序值比較
  5. 排序值越大,優先級越低

而對於同一個切面中的advice來説,就需要使用額外的判斷,這部分説實話不是特別明白,有興趣可以深入一下源碼,這裏就翻譯一下AspectJAwareAdvisorAutoProxyCreator#sortAdvisors方法的javadoc吧:

根據aspectj優先級對提供的advisor實例進行排序。如果兩個advice來自同一個advisor,那麼它們的順序是一樣的。同一個advisor的advice將使用如下規則進一步排序:如果任意一方是'After'類型的advice,那麼後聲明的優先級最高(即最後運行),否則先聲明的優先級最高(即最先運行)。

這其實也是符合常識邏輯的排序,即先聲明的先執行,但是由於切面的結構,在出方向上優先級最高的最後執行,所以最後聲明的'After'類型的advice因為優先級最高最後執行。

原始順序

那麼在排序值相同的情況下,不同advice的同類型的advice如何確定順序呢?我們可以看到執行advice之間排序的AbstractAdvisorAutoProxyCreator.sortAdvisors這個方法最終會使用List.sort(Comparator)來執行排序,查看該方法的javadoc可以得知使用的排序實現是從MergeSort優化而來的TimSort,它是一種穩定排序算法,因此可以推斷出如果排序值相同,那麼順序就取決於他們之間原來的順序。重新追溯回到原始順序的源頭:

// ========= AbstractAdvisorAutoProxyCreator =========
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 這一步獲取原始Advisor列表,往裏面走
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

// 該方法被子類AnnotationAwareAspectJAutoProxyCreator重寫
protected List<Advisor> findCandidateAdvisors() {
    Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
    // 使用工具類來獲取,繼續往裏走
    return this.advisorRetrievalHelper.findAdvisorBeans();
}

// AnnotationAwareAspectJAutoProxyCreator重寫的findCandidateAdvisors
@Override
protected List<Advisor> findCandidateAdvisors() {
    // 調用父類方法
    List<Advisor> advisors = super.findCandidateAdvisors();
    // 構造根據註解聲明的對應的advisor
    if (this.aspectJAdvisorsBuilder != null) {
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    return advisors;
}

// ========= AbstractAdvisorAutoProxyCreator =========

public List<Advisor> findAdvisorBeans() {
    // 獲取所有實現了Advisor接口的Bean名稱,同時做了緩存處理
    String[] advisorNames = this.cachedAdvisorBeanNames;
    if (advisorNames == null) {
        // 獲取相關的Bean名稱,進入此方法繼續走
        advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this.beanFactory, Advisor.class, true, false);
        this.cachedAdvisorBeanNames = advisorNames;
    }
    if (advisorNames.length == 0) {
        return new ArrayList<>();
    }

    List<Advisor> advisors = new ArrayList<>();
    // 按照獲取到的Bean名稱逐個添加到List中
    for (String name : advisorNames) {
        if (isEligibleBean(name)) {
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Skipping currently created advisor '" + name + "'");
                }
            }
            else {
                try {
                    advisors.add(this.beanFactory.getBean(name, Advisor.class));
                }
                catch (BeanCreationException ex) {
                    // 一些初始化相關的代碼 ...
                }
            }
        }
    }
    return advisors;
}

由上面的代碼可知,Advisor的原始順序取決於BeanFactory取出相關Bean名稱的順序,同時由註解描述構建出的Advisor全部排在原生Advisor實現類的後面。具體到SpringBoot的應用場景下,最後執行的內容為DefaultListableBeanFactory下的方法:

private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
    List<String> result = new ArrayList<>();

    // 根據beanDefinitionNames這個list成員進行遍歷
    for (String beanName : this.beanDefinitionNames) {
        if (!isAlias(beanName)) {
            try {
                RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                // Only check bean definition if it is complete.
                if (!mbd.isAbstract() && (allowEagerInit ||
                        (mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
                                !requiresEagerInitForType(mbd.getFactoryBeanName()))) {
                    // 省略匹配邏輯校驗代碼 ...
                    if (matchFound) {
                        result.add(beanName);
                    }
                }
            }
            // 省略異常處理代碼 ...
        }
    }

    // 根據manualSingletonNames這個list成員遍歷
    // 這裏主要包含一些手動控制、無需處理Bean生命週期的單例,比如SpringBootBanner
    // 通常Advisor實現類不會出現在這裏
    for (String beanName : this.manualSingletonNames) {
        try {
            // 省略代碼... 

            if (isTypeMatch(beanName, type)) {
                result.add(beanName);
            }
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Shouldn't happen - probably a result of circular reference resolution...
            logger.trace(LogMessage.format(
                    "Failed to check manually registered singleton with name '%s'", beanName), ex);
        }
    }

    return StringUtils.toStringArray(result);
}

最終我們可以看到原始數據的順序來源於BeanFactory的beanDefinitionNames這個List<String>類型的成員,這個集合包含了所有註冊的Bean名稱,並且維持了Bean註冊的順序,Bean註冊的細節此處就不展開了,我們可以直接在上述方法打斷點調試,根據SpringBoot運行調試結果可以看出如下幾點規則:

  1. 6個內置的Bean和SpringBoot啓動類最先註冊,內置Bean包括internalConfigurationAnnotationProcessorinternalAutowiredAnnotationProcessorinternalCommonAnnotationProcessorinternalEventListenerProcessorinternalEventListenerFactoryinternalCachingMetadataReaderFactory
  2. 接下來根據掃包獲取應用代碼中聲明的Bean,掃包順序通常是按照全類名的字母序(包括@Configuration修飾的配置類,但不包括其中定義的Bean)
  3. 然後是應用代碼中引入的Bean,包括配置類下聲明的Bean以及Import引入的Bean
  4. 最後是SpringBoot自動配置類相關的內容

總結

Spring AOP的核心類是AutoProxyCreator,它作為鈎子工具在Bean實例化時執行了代理創建操作,這也再一次表明Spring AOP在默認的proxy模式下使用的是動態代理,Spring只是借用了AspectJ的一些切面描述的註解,並沒有使用依賴特殊編譯器的靜態織入。

切面的順序是一個必須考慮的問題,Spring主要通過org.springframework.core.Ordered接口以及org.springframework.core.annotation.Order註解(還包括j2ee的Priority註解)來聲明排序值以控制順序。在SpringBoot應用環境下,您需要額外注意幾點:

  1. 開啓聲明式事務的註解@EnableTransactionManagement包含在自動配置類中,並沒有將order屬性直接暴露到配置文件中,其默認的順序值為Ordered.LOWEST_PRECEDENCE即最低順序。如果必須要配置,可以手動在應用配置類上主動使用@EnableTransactionManagement並修改order屬性值,但是這會讓配置文件中的一些相關配置失效。
  2. 同時使用聲明式事務和聲明式緩存並且不修改order值,由於聲明式緩存需要手動添加@EnableCaching,這使得默認情況下緩存Advisor的原始順序高於事務Advisor,因此先切面的入口處先執行緩存邏輯再執行事務邏輯,這恰好滿足一般的使用常理。
  3. 在開啓Spring AOP使用aspectj註解聲明切面時,如果不指定Order順序值,那麼默認順序值為最低順序,根據原始順序的邏輯,構建出來的切面全部位於原生切面的後面,也就是排在事務等框架提供的邏輯之後,您需要確保這是沒問題的,否則請顯式指定順序值。
  4. 不同的切面推薦放在不同的類中並指定順序值,防止意想不到的結果發生。
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.