一文搞懂 Spring事務是怎么通過AOP實現的 ,讓你醍醐灌頂


閱讀此文章需要掌握一定的AOP源碼基礎知識,可以更好的去理解事務,我在另外一篇文章有提過。

spring事務其實就是根據事務注解生成代理類,然后在前置增強方法里獲取connection,設置connectionthreadlocal,開啟事務。再執行原始方法,最后在后置增強方法中判斷有無異常來進行事務回滾或提交,再釋放連接。

對Spring中的事務功能的代碼進行分析,我們先從配置文件開始入手:在配置文件中我們是通過tx:annotation-driven的方式開啟的事務配置,所以我們先從這里開始進行分析,根據以往的經驗我們在自定義標簽中的解析過程中一定是做了一些操作,於是我們先從自定義標簽入手進行分析。使用IDEA搜索全局代碼,關鍵字annotation-driven,最終鎖定在類TxNamespaceHandler
image
主要查看TxNameSpaceHandlerinit方法。

  • 看源碼(TxNamespaceHandler.java)
@Override
public void init() {
    registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
    registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
    registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
}
  • 源碼分析

    在上述源碼中我們看到了annotation-driven這個標識,該行代碼也就是說:在遇到諸如tx:annotation-driven為開頭的配置后,Spring都會使用AnnotationDrivenBeanDefinitionParser類的parse方法進行解析處理。我們接下來跟蹤AnnotationDrivenBeanDefinitionParser類的parse方法

  • 看源碼(AnnotationDrivenBeanDefinitionParser.java)

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
    registerTransactionalEventListenerFactory(parserContext);
    String mode = element.getAttribute("mode");
    if ("aspectj".equals(mode)) {
        // mode="aspectj"
        registerTransactionAspect(element, parserContext);
        if (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader())) {
            registerJtaTransactionAspect(element, parserContext);
        }
    } else {
        // mode="proxy"
        AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
    }
    return null;
}
  • 源碼分析

    在上面的源碼中我們可以看到有對mode屬性進行一個解析判斷,根據代碼,如果我們需要使用Aspect的方式進行事務的切入(Spring中的事務是以AOP為基礎的),根據源碼的判斷條件我們可以看出我們在開始事務配置的時候也可以像如下這種方式進行開啟:

<!--開啟tx注解-->
<tx:annotation-driven transaction-manager="transactionManager" mode="aspectj"/>

事務代理類的創建

根據上面我們也可以知道,Spring事務會根據配置的`mode`不同,會有不同的實現。我們分開探索:
aspectj模式
  • 看方法registerJtaTransactionAspect
private void registerTransactionAspect(Element element, ParserContext parserContext) {
    String txAspectBeanName = TransactionManagementConfigUtils.TRANSACTION_ASPECT_BEAN_NAME;
    String txAspectClassName = TransactionManagementConfigUtils.TRANSACTION_ASPECT_CLASS_NAME;
    //如果沒有注冊過該類,就新注冊一個class為 AnnotationTransactionAspect 的bean
    if (!parserContext.getRegistry().containsBeanDefinition(txAspectBeanName)) {
        RootBeanDefinition def = new RootBeanDefinition();
        def.setBeanClassName(txAspectClassName);
        def.setFactoryMethodName("aspectOf");
        //把標簽里transaction-manager或transactionManager設置的值配置到bean的成員變量transactionManagerBeanName中
        registerTransactionManager(element, def);
        parserContext.registerBeanComponent(new BeanComponentDefinition(def, txAspectBeanName));
    }
}

看一下AnnotationTransactionAspect 類圖
image
我們發現AnnotationTransactionAspect和父類AbstractTransactionAspect都不是正常class,是aspect;之前設置的transactionManagerBeanName屬性在TransactionAspectSupport

接下來繼續追蹤AnnotationTransactionAspect

其實這里這種用法和@Aspect切面類里定義@Pointcut,@Around的用法一個效果

  • 看源碼(AnnotationTransactionAspect.java)
public aspect AnnotationTransactionAspect extends AbstractTransactionAspect {
    public AnnotationTransactionAspect() {
        super(new AnnotationTransactionAttributeSource(false));
    }
    /**
     * Matches the execution of any public method in a type with the Transactional
     * annotation, or any subtype of a type with the Transactional annotation.
     */
    /**
     *     作用在有Transactional注解或者包含Transactional的注解的public方法
     */
    private pointcut executionOfAnyPublicMethodInAtTransactionalType() :
            execution(public * ((@Transactional *)+).*(..)) && within(@Transactional *);
    /**
     * Matches the execution of any method with the Transactional annotation.
     */
    /**
     * 作用在任何有Transactional注解的方法
     */
    private pointcut executionOfTransactionalMethod() :
            execution(@Transactional * *(..));
    /**
     * Definition of pointcut from super aspect - matched join points
     * will have Spring transaction management applied.
     */
    /**
     * 父類抽象方法,滿足上面任一方法條件且滿足父類條件的
     */
    protected pointcut transactionalMethodExecution(Object txObject) :
            (executionOfAnyPublicMethodInAtTransactionalType() || executionOfTransactionalMethod() ) && this(txObject);
}
  • 看源碼(AbstractTransactionAspect.java)
public abstract aspect AbstractTransactionAspect extends TransactionAspectSupport implements DisposableBean {
    /**
     * Construct the aspect using the given transaction metadata retrieval strategy.
     * @param tas TransactionAttributeSource implementation, retrieving Spring
     * transaction metadata for each joinpoint. Implement the subclass to pass in
     * {@code null} if it is intended to be configured through Setter Injection.
     */
    protected AbstractTransactionAspect(TransactionAttributeSource tas) {
        setTransactionAttributeSource(tas);
    }
    @Override
        public void destroy() {
        // An aspect is basically a singleton -> cleanup on destruction
        clearTransactionManagerCache();
    }
    //!!!增強邏輯在這,事務也在這實現
    @SuppressAjWarnings("adviceDidNotMatch")
        Object around(final Object txObject): transactionalMethodExecution(txObject) {
        MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
        // Adapt to TransactionAspectSupport's invokeWithinTransaction...
        try {
            //父類方法
            return invokeWithinTransaction(methodSignature.getMethod(), txObject.getClass(), new InvocationCallback() {
                public Object proceedWithInvocation() throws Throwable {
                    //就是使用@Around注解時執行ProceedingJoinPoint.proceed()方法
                    return proceed(txObject);
                }
            }
            );
        }
        catch (RuntimeException | Error ex) {
            throw ex;
        }
        catch (Throwable thr) {
            Rethrower.rethrow(thr);
            throw new IllegalStateException("Should never get here", thr);
        }
    }
    /**
     * Concrete subaspects must implement this pointcut, to identify
     * transactional methods. For each selected joinpoint, TransactionMetadata
     * will be retrieved using Spring's TransactionAttributeSource interface.
     */
    /**
     * 由子類實現具體的pointcut
     */
    protected abstract pointcut transactionalMethodExecution(Object txObject);
    /**
     * Ugly but safe workaround: We need to be able to propagate checked exceptions,
     * despite AspectJ around advice supporting specifically declared exceptions only.
     */
    private static class Rethrower {
        public static void rethrow(final Throwable exception) {
            class CheckedExceptionRethrower<T extends Throwable> {
                @SuppressWarnings("unchecked")
                                private void rethrow(Throwable exception) throws T {
                    throw (T) exception;
                }
            }
            new CheckedExceptionRethrower<RuntimeException>().rethrow(exception);
        }
    }
}

總結

aspectj模式其實就是定義了一個Aspect,里面定義了切點,針對所有@Transaction注解的方法,並對切點進行@Around增強,會調用父類TransactionAspectSupportinvokeWithinTransaction方法。

proxy模式

我們從默認的配置方式進行分析(也就是不加mode="aspect"的方式)。對AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);進行分析。首先我們先看一下AopAutoProxyConfigurer類的configureAutoProxyCreator方法:

AopAutoProxyConfigurer類屬於AnnotationDrivenBeanDefinitionParser的內部類

  • 源碼分析(AnnotationDrivenBeanDefinitionParser.java)
private static class AopAutoProxyConfigurer {
    public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
        // 向IOC注冊 registerAutoProxyCreatorIfNecessary 這個類型的Bean
        // 具體是在 AopConfigUtils 的 registerAutoProxyCreatorIfNecessary 方法中定義的 registerAutoProxyCreatorIfNecessary
        AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
        String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;
        if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {
            Object eleSource = parserContext.extractSource(element);
            // Create the TransactionAttributeSource definition.
            // 創建 AnnotationTransactionAttributeSource 類型的Bean
            RootBeanDefinition sourceDef = new RootBeanDefinition(
                                    "org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
            sourceDef.setSource(eleSource);
            sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
            // Create the TransactionInterceptor definition.
            // 創建 TransactionInterceptor 類型的Bean
            RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
            interceptorDef.setSource(eleSource);
            interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            registerTransactionManager(element, interceptorDef);
            interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
            String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
            // Create the TransactionAttributeSourceAdvisor definition.
            // 創建 BeanFactoryTransactionAttributeSourceAdvisor 類型的Bean
            RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
            advisorDef.setSource(eleSource);
            advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            // 將上面 AnnotationTransactionAttributeSource 類型Bean注入進上面的Advisor
            advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
            // 將上面 TransactionInterceptor 類型Bean注入進上面的Advisor
            advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
            if (element.hasAttribute("order")) {
                advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
            }
            parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);
            // 將上面三個Bean注冊進IOC中
            CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
            compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
            compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
            compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));
            parserContext.registerComponent(compositeDef);
        }
    }
}
  • 源碼分析

    看上述源碼簡單總結一下:這里主要是注冊了三個Bean,分別是AnnotationTransactionAttributeSourceTransactionInterceptorBeanFactoryTransactionAttributeSourceAdvisor,還注冊了一個InfrastructureAdvisorAutoProxyCreator的bean;其中前三個Bean支撐了整個事務的功能。在這里我們簡單回顧一下AOP原理:

AOP原理:
AOP中有一個`Advisor`存放在代理類中,而Advisor中有`advise`與`pointcut`信息,每次執行被代理類的方法時都會執行代理類的`invoke`(如果是JDK代理)方法,而invoke方法會根據advisor中的pointcut動態匹配這個方法需要執行的advise鏈,遍歷執行advise鏈,從而達到AOP切面編程的目的。

需要注意的地方BeanFactoryTransactionAttributeSourceAdvisor.class,首先看到這個類的繼承結構,可以看的出來它其實是一個Advisor,這個類中有幾個關鍵的地方需要注意一下,在前面提到的一共注冊了三個Bean,將AnnotationTransactionAttributeSourceTransactionInterceptor這兩個屬性注入到了這個bean中:代碼如下:

// 將上面 AnnotationTransactionAttributeSource 類型Bean注入進上面的Advisor
advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
// 將上面 TransactionInterceptor 類型Bean注入進上面的Advisor
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);

問題來了:它們被注入成了什么呢?進入到BeanFactoryTransactionAttributeSourceAdvisor一看便知

@Nullable
private TransactionAttributeSource transactionAttributeSource;

BeanFactoryTransactionAttributeSourceAdvisor父類中AbstractBeanFactoryPointcutAdvisor有這樣的一個屬性

@Nullable
private String adviceBeanName;

從上面可以大概知道,先將TransactionInterceptor的BeanName傳入到注入到Advisor,然后將AnnotationTransactionAttributeSource這個Bean注入Advisor。那么這個Source Bean有什么用呢?我們繼續追蹤BeanFactoryTransactionAttributeSourceAdvisor.java的源碼:

  • 看源碼(BeanFactoryTransactionAttributeSourceAdvisor.java)
private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
    @Override
    @Nullable
    protected TransactionAttributeSource getTransactionAttributeSource() {
        return transactionAttributeSource;
    }
};
  • 源碼解析

看到這里應該明白了,這里的Source提供了Pointcut信息,作為存放事務屬性的一個類注入到Advisor中。進行到這里我們也知道了這三個BeanAnnotationTransactionAttributeSourceTransactionInterceptorBeanFactoryTransactionAttributeSourceAdvisor,的作用了。簡單來說就是先注冊pointcut、advice、advisor,然后將pointcut和advice注入到advisor中,在之后動態代理的時候會使用這個Advisor去尋找每個Bean是否需要動態代理(取決與是否有開啟事務),因為Advisor中有pointcut信息。
image

  • InfrastructureAdvisorAutoProxyCreator:在方法的開頭,首先調用了AopConfigUtils去注冊了這個Bean,那么這個Bean是做什么的?首先還是看一下這個類的結構。
    image
    這個類繼承了AbstractAutoProxyCreator,看到這個名字,結合之前說過的AOP的應該知道它是做什么的了。其次這個類還實現了BeanPostProcessor接口,凡是實現了這個BeanPostProcessor接口的類,我們首先關注的就是它的postProcessAfterInitialization方法,這個在其父類也就是剛剛提到的AbstractAutoProxyCreator去實現的(這里需要知道Spring容器初始化Bean的過程,關於BeanPostProcessor的使用后續講解,如果不知道只需要了解如果一個Bean實現了BeanPostProcessor接口,當所有Bean實例化且依賴注入之后初始化方法之后會執行這個實現Bean的postProcessAfterInitialization方法)。

接下來進入這個函數:

  • 看源碼(AopNamespaceUtils.java)
public static void registerAutoProxyCreatorIfNecessary(
            ParserContext parserContext, Element sourceElement) {
    BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(
                    parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    registerComponentIfNecessary(beanDefinition, parserContext);
}

繼續追蹤上面的源碼中的registerComponentIfNecessary方法

  • 看源碼(AopNamespaceUtils.java)
private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) {
    if (beanDefinition != null) {
        parserContext.registerComponent(
                            new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME));
    }
}
  • 源碼分析

對於上面解析來的代碼流程已經在AOP中有所分析,可以自行翻看AOP的文章,上面的兩個函數主要的目的是注冊了InfrastructureAdvisorAutoProxyCreator類型的Bean,那么注冊這個類的目的是什么呢?再次查回顧這個類的層次結構:
image
分析這個類的層次結構:InfrastructureAdvisorAutoProxyCreator這個類間接實現了SmartInstantiationAwareBeanPostProcessor接口,而SmartInstantiationAwareBeanPostProcessor這個接口有繼承了InstantiationAwareBeanPostProcessor接口。也就是說在Spring中,所有Bean實例化時Spring都會保證調用其postProcessAfterInstantiation方法。其實現是在其父類AbstractAutoProxyCreator中。

接下來一之前AccountByXMLServiceImpl為例,當實例化AccountByXmlServiceImpl的Bean時便會調用下面這個方法,方法如下:

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        // 根據bean的class 和 name構建出一個key  格式:beanClassName_beanName
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            // 如果它適合被代理,則需要指定封裝bean
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

這里實現的主要目的就是對指定的Bean進行封裝,當然首先要確定是否需要封裝,檢測與封裝的工作都委托給了wrapIfNecessary函數進行:

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;
    }
    // 給定的bean類是否是一個基礎設施類,基礎設施類不應該被代理,或者配置了指定的bean不需要代理
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
    // 獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序)
    // Create proxy if we have advice.
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    // 如果有 Advisor,則進行下面的動態代理創建過程
    if (specificInterceptors != DO_NOT_PROXY) {
        // 如果獲取到了增強則需要針對增強進行代理
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 創建代理 JDK 動態代理或者 CGLIB 動態代理
        Object proxy = createProxy(
                            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        // 將代理對象的 Class 對象(目標類的子類)保存
        this.proxyTypes.put(cacheKey, proxy.getClass());
        //  返回這個 Bean 對象
        return proxy;
    }
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

wrapIfNecessary函數看上去比較復雜,但是邏輯相對還是比較簡單的,在wrapIfNecessary函數中主要的工作如下:

  • 找出指定bean對應的增強器
  • 根據找出的增強器創建代理

聽起來挺簡單的邏輯,但是Spring又做了哪些復雜的工作呢?對於創建代理的工作,通過之前AOP的文章分析相信大家已經有所熟悉了。但是對於增強器的獲取,Spring又是如何操作的呢?

獲取對應class/method的增強器

尋找候選的增強

獲取指定Bean對應的增強器,其中包含了兩個關鍵字:增強器對應。也就是說在getAdvicesAndAdvisorsForBean函數(上面wrapIfNecessary函數里面的方法)中,不僅要找出增強器,而且要判斷增強器是否滿足要求。接下來看一下源碼:

  • 看源碼(AbstractAdvisorAutoProxyCreator.java)
@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;
    }
    return advisors.toArray();
}

繼續查看一下上面方法中的findEligibleAdvisors函數:

  • 看源碼(AbstractAdvisorAutoProxyCreator.java)
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

findEligibleAdvisors函數中我們發現了findCandidateAdvisorsfindAdvisorsThatCanApply這兩個函數;其中findCandidateAdvisors這個函數是尋找候選的增強,我們簡單掃一下這個源碼:

  • 看源碼(AbstractAdvisorAutoProxyCreator.java)
protected List<Advisor> findCandidateAdvisors() {
    Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
    return this.advisorRetrievalHelper.findAdvisorBeans();
}

接着追蹤里面的findAdvisorBeans函數

  • 看源碼(BeanFactoryAdvisorRetrievalHelper.java)
public List<Advisor> findAdvisorBeans() {
    // Determine list of advisor bean names, if not cached already.
    String[] advisorNames = this.cachedAdvisorBeanNames;
    if (advisorNames == null) {
        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let the auto-proxy creator apply to them!
        // 獲取BeanFactory中所有對應Advisor.class的類名
        // 這里和AspectJ的方式有點不同,AspectJ是獲取所有的Object.class,然后通過反射過濾有注解AspectJ的類
        advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                            this.beanFactory, Advisor.class, true, false);
        this.cachedAdvisorBeanNames = advisorNames;
    }
    if (advisorNames.length == 0) {
        return new ArrayList<>();
    }
    List<Advisor> advisors = new ArrayList<>();
    for (String name : advisorNames) {
        if (isEligibleBean(name)) {
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Skipping currently created advisor '" + name + "'");
                }
            } else {
                try {
                    //直接獲取advisorNames的實例,封裝進advisors數組
                    advisors.add(this.beanFactory.getBean(name, Advisor.class));
                }
                catch (BeanCreationException ex) {
                    Throwable rootCause = ex.getMostSpecificCause();
                    if (rootCause instanceof BeanCurrentlyInCreationException) {
                        BeanCreationException bce = (BeanCreationException) rootCause;
                        String bceBeanName = bce.getBeanName();
                        if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
                            if (logger.isTraceEnabled()) {
                                logger.trace("Skipping advisor '" + name +
                                                                            "' with dependency on currently created bean: " + ex.getMessage());
                            }
                            // Ignore: indicates a reference back to the bean we're trying to advise.
                            // We want to find advisors other than the currently created bean itself.
                            continue;
                        }
                    }
                    throw ex;
                }
            }
        }
    }
    return advisors;
}
  • 源碼分析

首先這個findAdvisorBeans函數先通過BeanFactoryUtils類提供的工具方法獲取對應的Advisor.class,獲取的辦法就是使用ListableBeanFactory中提供的方法:

String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
**在我們講解自定義標簽時曾經注冊了一個類型為`BeanFactoryTransactionAttributeSourceAdvisor`的Bean**,而在此bean中我們又注入了另外兩個Bean,那么此時這個Bean就會開始被使用,因為**BeanFactoryTransactionAttributeSourceAdvisor**同樣也實現了Advisor接口。那么在獲取所有增強器時自然也會將此Bean提取出來,並隨着其他增強器一起在后續的步驟中被植入代理。
候選增強器中尋找匹配項

當找出對應的增強器后,接下來的任務就是看這些增強器是否與對應的class匹配了,當然不只是class,class內部的方法如果匹配也可以通過驗證。

接下來看在findEligibleAdvisors函數中我的findAdvisorsThatCanApply這個函數:

  • 看源碼(AbstractAdvisorAutoProxyCreator.java)
protected List<Advisor> findAdvisorsThatCanApply(
            List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
    ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    try {
        // 過濾已經得到的advisors
        return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    }
    finally {
        ProxyCreationContext.setCurrentProxiedBeanName(null);
    }
}

繼續追蹤里面的findAdvisorsThatCanApply方法

  • 看源碼(AopUtils)
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    List<Advisor> eligibleAdvisors = new ArrayList<>();
    // 首先處理引介增強
    /*
         * 引介增強是一種特殊的增強,其它的增強是方法級別的增強,即只能在方法前或方法后添加增強。
         * 而引介增強則不是添加到方法上的增強, 而是添加到類方法級別的增強,即可以為目標類動態實現某個接口,
         * 或者動態添加某些方法。我們通過下面的事例演示引介增強的使用
         */
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
            eligibleAdvisors.add(candidate);
        }
    }
    Boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    for (Advisor candidate : candidateAdvisors) {
        // 引介增強已經處理
        if (candidate instanceof IntroductionAdvisor) {
            // already processed
            continue;
        }
        // 對於普通bean的 進行處理
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    return eligibleAdvisors;
}

繼續追蹤該方法中的canApply方法:

public static Boolean canApply(Advisor advisor, Class<?> targetClass, Boolean hasIntroductions) {
    if (advisor instanceof IntroductionAdvisor) {
        return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    } else if (advisor instanceof PointcutAdvisor) {
        PointcutAdvisor pca = (PointcutAdvisor) advisor;
        return canApply(pca.getPointcut(), targetClass, hasIntroductions);
    } else {
        // It doesn't have a pointcut so we assume it applies.
        return true;
    }
}
  • 源碼分析

回想之前,因為BeanFactoryTransactionAttributeSourceAdvisor間接實現了PointcutAdvisor
image
所以在canApply函數中的第二個if判斷是就會通過判斷。會將BeanFactoryTransactionAttributeSourceAdvisor中的getPointcut()方法返回值作為參數繼續調用canApply方法,而getPoint()方法返回的是TransactionAttributeSourcePointcut實例,對於transactionAttributeSource這個屬性相信大家還有印象,就是在解析自定義標簽時注入進去的,方法如下

private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
    @Override
            @Nullable
            protected TransactionAttributeSource getTransactionAttributeSource() {
        return transactionAttributeSource;
    }
}
;

那么,使用TransactionAttributeSourcePointcut類型的實例作為函數繼續追蹤canApply

  • 源碼(AopUtils.java)
public static Boolean canApply(Pointcut pc, Class<?> targetClass, Boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");
    // 通過Pointcut的條件判斷此類是否匹配
    if (!pc.getClassFilter().matches(targetClass)) {
        return false;
    }
    // 此時的pc表示TransactionAttributeSourcePointcut
    // pc.getMethodMatcher()返回的正是自身(this)
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    if (methodMatcher == MethodMatcher.TRUE) {
        // No need to iterate the methods if we're matching any method anyway...
        return true;
    }
    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
        introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }
    Set<Class<?>> classes = new LinkedHashSet<>();
    if (!Proxy.isProxyClass(targetClass)) {
        classes.add(ClassUtils.getUserClass(targetClass));
    }
    //獲取對應類的所有接口
    classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    //對類進行遍歷
    for (Class<?> clazz : classes) {
        // 反射獲取類中所有的方法
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            // 根據匹配原則判斷該方法是否能匹配Pointcut中的規則,如果有一個方法匹配則返回true
            if (introductionAwareMethodMatcher != null ?
                                    introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                                    methodMatcher.matches(method, targetClass)) {
                return true;
            }
        }
    }
    return false;
}

通過上面的函數大致可以理清大致的脈絡:

首先獲取對應類的所有接口並連通本類一起遍歷,遍歷過程中又對類中的方法再次遍歷,一旦匹配成功便認為這個類適用於當前增強器。

到這里我們就會有疑問?**對於事務的配置不僅僅局限在函數配置上,我們都知道,在類或接口的配置上可以延續到類中的每個函數上。那么,如果針對每個函數進行檢測,在本類身上配置的事務屬性豈不是檢測不到了嗎?**接下來我們帶着這個疑問繼續探索canApply函數中的`matcher`的方法。

做匹配的時候`methodMatcher.matches(method, targetClass)`會使用`TransactionAttributeSourcePointcut`類的`matches`方法。
  • 看源碼(TransactionAttributeSourcePointcut.java)
@Override
public Boolean matches(Method method, Class<?> targetClass) {
    // 自定義標簽解析時注入
    TransactionAttributeSource tas = getTransactionAttributeSource();
    return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}
  • 源碼分析

此時上述源碼中的tas標識AnnotationTransactionAttributeSource 類型,這里會判斷tas.getTransactionAttribute(method, targetClass),而AnnotationTransactionAttributeSource 類型的getTransactionAttribute方法如下:

  • 看源碼(AbstractFallbackTransactionAttributeSource.java)
@Override
@Nullable
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    if (method.getDeclaringClass() == Object.class) {
        return null;
    }
    // First, see if we have a cached value.
    Object cacheKey = getCacheKey(method, targetClass);
    TransactionAttribute cached = this.attributeCache.get(cacheKey);
    //先從緩存中獲取TransactionAttribute
    if (cached != null) {
        // Value will either be canonical value indicating there is no transaction attribute,
        // or an actual transaction attribute.
        if (cached == NULL_TRANSACTION_ATTRIBUTE) {
            return null;
        } else {
            return cached;
        }
    }
    // 如果緩存中沒有,工作又委托給了computeTransactionAttribute函數 else {
        // We need to work it out.
        TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
        // Put it in the cache.
        if (txAttr == null) {
            // 設置為空
            this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
        } else {
            String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
            if (txAttr instanceof DefaultTransactionAttribute) {
                DefaultTransactionAttribute dta = (DefaultTransactionAttribute) txAttr;
                dta.setDescriptor(methodIdentification);
                dta.resolveAttributeStrings(this.embeddedValueResolver);
            }
            if (logger.isTraceEnabled()) {
                logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
            }
            //加入緩存中
            this.attributeCache.put(cacheKey, txAttr);
        }
        return txAttr;
    }
}
  • 源碼分析

嘗試從緩存加載,如果對應信息沒有緩存的話,工作有委托給了computeTransactionAttribute函數,在computeTransactionAttribute函數中我們終於看到了事務標簽的提取過程。

  • 看源碼(AbstractFallbackTransactionAttributeSource.java)
@Nullable
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    // Don't allow no-public methods as required.
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
    }
    // The method may be on an interface, but we need attributes from the target class.
    // If the target class is null, the method will be unchanged.
    // method代表接口中的方法,specificMethod代表實現類中的方法
    Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
    // First try is the method in the target class.
    // 查看方法中是否存在事務聲明
    TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
    if (txAttr != null) {
        return txAttr;
    }
    // Second try is the transaction attribute on the target class.
    // 查看方法所在類中是否存在事務聲明
    txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
    if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
        return txAttr;
    }
    // 如果存在接口,則到接口中去尋找
    if (specificMethod != method) {
        // Fallback is to look at the original method.
        // 查找接口方法
        txAttr = findTransactionAttribute(method);
        if (txAttr != null) {
            return txAttr;
        }
        // Last fallback is the class of the original method.
        // 到接口中的類中去尋找
        txAttr = findTransactionAttribute(method.getDeclaringClass());
        if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
            return txAttr;
        }
    }
    return null;
}
  • 源碼分析

對於事務屬性的獲取規則相信大家都已經熟悉很清楚,如果方法中存在事務屬性,則使用方法上的屬性,否則使用方法所在類上的屬性,如果方法所在類的屬性還是沒有搜尋到對應的事務屬性,那么搜尋接口中的方法,再沒有的話,最后嘗試搜尋接口類上面的聲明。對於函數computeTransactionAttribute中的邏輯與我們所認識的規則並無差別,但是上面函數中並沒有真正的去做搜尋事務屬性的邏輯,而是搭建了個執行框架,將搜尋事務屬性的任務委托給了findTransactionAttribute方法去執行。繼續進行分析:

  • 看源碼(AnnotationTransactionAttributeSource.java)
@Override
@Nullable
protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
    return determineTransactionAttribute(clazz);
}

繼續查看上面的determineTransactionAttribute函數:

  • 看源碼(AnnotationTransactionAttributeSource.java)
@Nullable
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
    for (TransactionAnnotationParser parser : this.annotationParsers) {
        TransactionAttribute attr = parser.parseTransactionAnnotation(element);
        if (attr != null) {
            return attr;
        }
    }
    return null;
}
  • 源碼分析

this.annotationParsers是在當前類**AnnotationTransactionAttributeSource **初始化的時候初始化的,其中的值被加入了SpringTransactionAnnotationParser,也就是當進行屬性獲取的時候虎其實是使用可SpringTransactionAnnotationParser類的parseTransactionAnnotation 方法進行解析的。繼續分析源碼

  • 看源碼(SpringTransactionAnnotationParser.java)
@Override
@Nullable
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
    AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
                    element, Transactional.class, false, false);
    if (attributes != null) {
        return parseTransactionAnnotation(attributes);
    } else {
        return null;
    }
}
  • 源碼分析

至此,我們終於看到了想看到的獲取注解標記的代碼,首先會判斷當前的類是否包含又Transactional注解,這是事務屬性的基礎,當然如果有的話會繼續調用parseTransactionAnnotation方法解析詳細的屬性。接着看源碼:

  • 看源碼()
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
    RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
    // 解析propagation
    Propagation propagation = attributes.getEnum("propagation");
    rbta.setPropagationBehavior(propagation.value());
    Isolation isolation = attributes.getEnum("isolation");
    // 解析isolation
    rbta.setIsolationLevel(isolation.value());
    // 解析timeout
    rbta.setTimeout(attributes.getNumber("timeout").intValue());
    String timeoutString = attributes.getString("timeoutString");
    Assert.isTrue(!StringUtils.hasText(timeoutString) || rbta.getTimeout() < 0,
                    "Specify 'timeout' or 'timeoutString', not both");
    rbta.setTimeoutString(timeoutString);
    // 解析readOnly
    rbta.setReadOnly(attributes.getBoolean("readOnly"));
    // 解析value
    rbta.setQualifier(attributes.getString("value"));
    rbta.setLabels(Arrays.asList(attributes.getStringArray("label")));
    List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
    // 解析rollbackFor
    for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
        rollbackRules.add(new RollbackRuleAttribute(rbRule));
    }
    // 解析rollbackForClassName
    for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
        rollbackRules.add(new RollbackRuleAttribute(rbRule));
    }
    // 解析noRollbackFor
    for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
        rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
    }
    // 解析noRollbackForClassName
    for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
        rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
    }
    rbta.setRollbackRules(rollbackRules);
    return rbta;
}
  • 源碼解析

至此,我們終於完成了事務標簽的解析,回顧一下,我們現在的任務是找出某個增強其是否適合於對應的類,而是否匹配的關鍵則在於是否從指定的類或類中的方法中找到對應的事務屬性。現在我們之前的AccountByXMLServiceImpl為例,已經在它的接口AccountByXMLServiceImpl中找到了事務屬性,所以,它是與事務增強器匹配的,也就是它會被事務功能修飾。

至此,事務功能的初始化工作便結束了,當判斷某個bean適用於事務增強時,也就i是適用於增強器`BeanFactoryTransactionAttributeSourceAdvisor`,**BeanFactoryTransactionAttributeSourceAdvisor**作為Advisor的實現類,自然要遵從Advisor的處理方式,當代理被調用時會調用這個類的增強方法,也就是bean的Advice,又因為在解析事務定義標簽時我們把`Transactionlnterceptor`類的Bean注入到了`BeanFactoryTransactionAttributeSourceAdvisor`中,所以,在調用事務增強器增強的代理類時會首先執行**Transactionlnterceptor**進行增強,同時,也就是在Transactionlnterceptor類中的invoke方法中完成了整個事務的邏輯。

總結:

這一篇文章主要將了事務的Advisor是如何注冊進Spring容器的,也講解了Spring是如何將有配置事務的類配置上事務的,實際上就是使用了AOP那一套,也講解了Advisor和Pointcut驗證流程。至此事務的初始化工作已經完成,在之后的調用過程,如果代理類的方法被調用,都會調用BeanFactoryTransactionAttributeSourceAdvisor這個Advisor的增強方法。目前就是我們還沒有提到的那個Advisor里面的Advice;還記得嗎我們在自定義標簽的時候我們將TransactionInterceptor這個Advice作為Bean注入到IOC容器中,並且將其注入到Advisor中,這個Advice在代理類的invoke方法中會被封裝到攔截器鏈中,最終事務的功能都能在Advice中體現。

微信搜索【碼上遇見你】獲取更多精彩內容。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM