【spring源碼分析】spring事務(一):認識事務組件

注:本系列源碼分析基於spring 5.2.2.RELEASE,本文的分析基於 annotation 註解方式,gitee倉庫鏈接:funcy/spring-framework.

前面分析完了spring aop相關功能後,本文將來分析spring aop的一個應用——事務管理。

1. 從兩個demo講起

在正式分析前,我們先來思考下,如果讓我們自己來基於spring aop來設計一套事務處理機制,該如何實現呢?如果沒有spring,我們的事務處理代碼一般長這樣:

public void fun() {
    // 開啓事務
    start();
    try {
        // 業務處理
        xxx();
        // 提交事務
        commit();
    } catch(Exception e) {
        // 回滾事務
        rollback();
        throw e;
    }
}

從上面的代碼來看,像開啓事務、提交事務、回滾事務,都跟業務代碼無關,這些可以使用spring aop來實現,因此就有了下面兩個demo.

demo01:基於 @Around 註解實現事務

咱們可以使用@Around註解來操作,代碼如下:

  1. 定義一個註解:@MyTransactional
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyTransactional {
}
  1. 定義 aop 操作
@Aspect
@Component
public class MyAopAspectj {
    @Pointcut("@annotation(org.springframework.learn.tx.demo02.MyTransactional)")
    public void testAop(){

    }

    @Around("testAop()")
    public Object around(ProceedingJoinPoint p) throws Throwable {
        System.out.println("執行前,開啓事務....");
        try {
            Object o = p.proceed();
            System.out.println("執行完成,提交事務....");
            return o;
        } catch (Throwable e) {
            System.out.println("出現了異常,根據異常類型回滾事務....");
            throw e;
        } finally {
            System.out.println("執行後....");
        }
    }

}
  1. config,進行一些必要的配置
@Configuration
@ComponentScan("org.springframework.learn.tx.demo02")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class TxDemo02Config {

}
  1. 添加一個service類,其中一個方法上有@MyTransactional註解
@Service
public class TxTestService {

    @MyTransactional
    public void test01() {
        System.out.println("執行test01方法");
    }

    public void test02() {
        System.out.println("執行test02方法");
    }

}
  1. 主類
public class TxDemo02Main {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(TxDemo02Config.class);
        TxTestService service = applicationContext.getBean(TxTestService.class);
        System.out.println("-------------------");
        service.test01();
        System.out.println("-------------------");
        service.test02();

    }
}

運行,結果如下:

-------------------
執行前,開啓事務....
執行test01方法
執行完成,提交事務....
執行後....
-------------------
執行test02方法

這個demo中,我們使用@Around註解來攔截業務代碼執行前後的情況,可以看到,@Around註解可以在代碼運行前後甚至是出現異常時處理一些額外的操作。

demo02:自定義advisor實現事務

讓我們回憶下spring aop 對@Around註解的處理,實際上@Around最終會封裝爲InstantiationModelAwarePointcutAdvisorImpl對象,後面的處理就跟@Around無關了,@AroundInstantiationModelAwarePointcutAdvisorImpl對象的過程,可參考spring aop之AnnotationAwareAspectJAutoProxyCreator 分析(上).

InstantiationModelAwarePointcutAdvisorImpl 是個什麼東西呢?這是個advisor,具體來說就是可用於方法的增強。關於spring aop是如何找到能應用於當前方法的advisor的,可參考spring aop 之 AnnotationAwareAspectJAutoProxyCreator 分析(下)

通過以上分析,給我們提供了另一個思路:我們可以實現advisor接口,定製化自己的邏輯,代碼如下:

  1. 準備advice
/**
 * 這個advice就是advisor的一個屬性,切面邏輯在這裏處理
 */
public class MyAdvice implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("執行前,開啓事務....");
        try {
            Object val = invocation.proceed();
            System.out.println("執行完成,提交事務....");
            return val;
        } catch (Throwable e) {
            System.out.println("出現了異常,根據異常類型回滾事務....");
            throw e;
        } finally {
            System.out.println("執行後....");
        }
    }
}
  1. 準備pointcut
/**
 * 切點
 * 判斷哪些方法能用於該advisor
 */
public class MyPointcut extends StaticMethodMatcherPointcut {
    /**
     * 匹配方法,有 @MyTransactional 的類或方法就返回true
     */
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return null != AnnotationUtils.getAnnotation(method, MyTransactional.class)
                || null != AnnotationUtils.getAnnotation(targetClass, MyTransactional.class);
    }
}
  1. 準備advisor
/**
 * advisor 可看作是 advice 與 pointcut 的包裝
 */
@Component
public class MyAdvisor extends AbstractBeanFactoryPointcutAdvisor {

    private static final long serialVersionUID = 2651364800145442305L;

    private MyPointcut pointcut;

    public MyAdvisor() {
        this.pointcut = new MyPointcut();
        this.setAdvice(new MyAdvice());
    }

    @Override
    public Pointcut getPointcut() {
        return this.pointcut;
    }

}

以上是不同於註解的實現方式,接下來的代碼就與註解一樣了。

  1. 準備一個註解:@MyTransactional
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyTransactional {
}
  1. 處理項目配置
@Configuration
@ComponentScan("org.springframework.learn.tx.demo01")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class TxDemo01Config {

}
  1. 準備一個service
@Service
public class TxTestService {

    @MyTransactional
    public void test01() {
        System.out.println("執行test01方法");
    }

    public void test02() {
        System.out.println("執行test02方法");
    }

}
  1. 主類
public class TxDemo01Main {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(TxDemo01Config.class);
        TxTestService service = applicationContext.getBean(TxTestService.class);
        System.out.println("-------------------");
        service.test01();
        System.out.println("-------------------");
        service.test02();

    }
}

運行,結果如下:

-------------------
執行前,開啓事務....
執行test01方法
執行完成,提交事務....
執行後....
-------------------
執行test02方法

2. 使用spring事務管理

有了前面的兩個小demo作爲開胃菜,對於spring事務處理想必你已有了一個清晰的認識,spring在處理事務時,使用的就是第二種方式,即往自定義一個advisor添加到spring容器中。關於spring實現任務的具體細節,我們待會分析,這裏我們再上一個demo,體驗下我們平時是怎麼使用事務的。

爲了進行數據庫連接,我們需要引入數據庫連接池,這裏我們使用的是mysql,需要在spring-learn.gradle中添加依賴:

optional("mysql:mysql-connector-java:5.1.48")

接着就是代碼了。

  1. 配置類
@Configuration
@ComponentScan("org.springframework.learn.tx.demo03")
@EnableTransactionManagement(proxyTargetClass = true)
public class TxDemo01Config {

    /**
     * 生成數據源
     * @return
     * @throws Exception
     */
    @Bean
    public DataSource dataSource() throws Exception {
        Driver driver = new com.mysql.jdbc.Driver();
        String url = "jdbc:mysql://localhost:3306/test";
        String username = "root";
        String password = "123";
        return new SimpleDriverDataSource(driver, url, username, password);
    }

    /**
     * 生成jdbcTemplate,後面就是用這個類來處理數據庫的操作
     * @param dataSource
     * @return
     */
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    /**
     * 事務管理器
     * @param dataSource
     * @return
     */
    @Bean
    public DataSourceTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

}
  1. 數據庫操作類
@Service
public class UserService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /**
     * 數據庫插入操作,使用 @Transactional 開啓事務
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int insert() {
        String sql = "insert into `user`(`login_name`, `nick`, `create_time`, `update_time`)"
                + "values (?, ?, ?, ?)";
        int result = jdbcTemplate.update(sql, "test", "test", new Date(), new Date());
        if(true) {
            //throw new RuntimeException("拋出個異常");
        }
        System.out.println(result);
        return result;
    }

}
  1. 主類
public class TxDemo01Main {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(TxDemo01Config.class);
        UserService userService = applicationContext.getBean(UserService.class);
        userService.insert();

    }
}

demo中,DataSource使用spring自帶的SimpleDriverDataSourceorm框架也是spring提供的jdbcTemplate,使用的user表sql如下:

CREATE TABLE `user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
  `login_name` varchar(32) NOT NULL DEFAULT '0' COMMENT '登錄名',
  `nick` varchar(32) NOT NULL DEFAULT '0' COMMENT '暱稱',
  `create_time` datetime DEFAULT NULL COMMENT '創建時間',
  `update_time` datetime DEFAULT NULL COMMENT '更新時間',
  PRIMARY KEY (`id`),
  KEY `create_time` (`create_time`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='用戶表';

執行結果如下:

第一次不拋出異常,數據庫結果:

第二次拋出異常,數據庫結果:

第三次不拋出異常,數據庫結果:

可以看到,第二次拋出異常時,數據正常回滾了。

我們再來分析下這個demo,跟事務有關的代碼有三處:

  • @EnableTransactionManagement(proxyTargetClass = true):啓用事務
  • DataSourceTransactionManager:事務管理器
  • @Transactional:指定開啓事務的方法

類比於aop的@EnableAspectJAutoProxy@EnableTransactionManagement是啓動事務的入口,接着我們就從這個註解入手,分析spring 事務的啓用流程。

3. @EnableTransactionManagement 註解

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

    /**
     * 學完aop後,想必對這個屬性已經很熟悉
     * true: 表示強制使用cglib代理
     * false:如果目標類實現了接口,則使用jdk動態代理,否則使用cglib代理
     * 僅在 mode 爲 PROXY 下生效
     */
    boolean proxyTargetClass() default false;

    /**
     * advice模式,使用代理,還是使用 aspectJ
     */
    AdviceMode mode() default AdviceMode.PROXY;

    /**
     * 執行順序,當一個代理對象有多個增強時,按什麼樣的順序來執行
     */
    int order() default Ordered.LOWEST_PRECEDENCE;

}

這個註解本身沒什麼,就三個屬性,註釋已經很明確了,我們關鍵還是看這個註解引入的類:TransactionManagementConfigurationSelector

public class TransactionManagementConfigurationSelector extends 
        AdviceModeImportSelector<EnableTransactionManagement> {
    @Override
    protected String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:
                // 基於代理的事務管理,會引入兩個類
                return new String[] {AutoProxyRegistrar.class.getName(),
                        ProxyTransactionManagementConfiguration.class.getName()};
            case ASPECTJ:
                // 基於aspectJ的事務管理,會引入這個類,本文不分析
                return new String[] {determineTransactionAspectClass()};
            default:
                return null;
        }
    }
    // 省略其他
    ...

}

基於代理的事務管理,會引入兩個類:AutoProxyRegistrarProxyTransactionManagementConfiguration,接下來我們就來分析這兩個類。

3.1 AutoProxyRegistrar

從名字上來看,AutoProxyRegistrar是一個註冊器,還記得前面aop的註冊器AspectJAutoProxyRegistrar嗎,兩者一樣的套路!

我們來看看裏面究竟做了啥:

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條件的,就是 @EnableTransactionManagement 註解
            if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
                    Boolean.class == proxyTargetClass.getClass()) {
                candidateFound = true;
                if (mode == AdviceMode.PROXY) {
                    // 註冊操作,最終註冊了 InfrastructureAdvisorAutoProxyCreator 類,接下來會繼續分析
                    AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                    if ((Boolean) proxyTargetClass) {
                        // 使用cglib代理
                        AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                        return;
                    }
                }
            }
        }
        if (!candidateFound && logger.isInfoEnabled()) {
            String name = getClass().getSimpleName();
            logger.info(...);
        }
    }
}

這行代碼關鍵的就只有if裏的幾行,先說下if條件:mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&Boolean.class == proxyTargetClass.getClass(),通過上面的對@EnableTransactionManagement,這說的就是它了;對於mode == AdviceMode.PROXY,繼續調用AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)(關於這個方法的調用,我們接下來會繼續分析);然後處理proxyTargetClass,這個屬性的作用與@EnableAspectJAutoProxy中的proxyTargetClass一致,也是可以強制使用cglib代理。

接着我們來分析下AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)的過程,跟進代碼:

AopConfigUtils

    @Nullable
    public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
        // 繼續往下看
        return registerAutoProxyCreatorIfNecessary(registry, null);
    }

    @Nullable
    public static BeanDefinition registerAutoProxyCreatorIfNecessary(
            BeanDefinitionRegistry registry, @Nullable Object source) {
        // 傳入 InfrastructureAdvisorAutoProxyCreator 類,繼續調用
        return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, 
            registry, source);
    }

看到這裏,是不是有種深深的熟悉感?aop中的AspectJAnnotationAutoProxyCreator也 是這麼註冊的!進入AopConfigUtils#registerOrEscalateApcAsRequired方法:

// AopConfigUtils 可註冊的類都在這裏了
private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<>(3);

static {
    APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
    APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
    APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
}

/**
 * 註冊操作
 */
private static BeanDefinition registerOrEscalateApcAsRequired(
        Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    //如果已存在這個bean
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        //判斷優先級,如果優先級較高則替換原先的bean
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
            // 已存在類 的優先級 小於正在註冊的,則使用正在註冊的,已存在的三個類的優先級爲
            // 0: InfrastructureAdvisorAutoProxyCreator(處理事務)
            // 1: AspectJAwareAdvisorAutoProxyCreator(處理基於xml的aop)
            // 2: AnnotationAwareAspectJAutoProxyCreator(處理基於註解的aop)
            if (currentPriority < requiredPriority) {
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        return null;
    }
    //註冊XxxAutoProxyCreator到容器中
    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;
}

/**
 * 查找註冊類的優先級
 */
private static int findPriorityForClass(@Nullable String className) {
    for (int i = 0; i < APC_PRIORITY_LIST.size(); i++) {
        Class<?> clazz = APC_PRIORITY_LIST.get(i);
        if (clazz.getName().equals(className)) {
            return i;
        }
    }
    throw new IllegalArgumentException(
            "Class name [" + className + "] is not a known auto-proxy creator class");
}

AopConfigUtils可註冊的類有有三個:

  • InfrastructureAdvisorAutoProxyCreator:處理事務
  • AspectJAwareAdvisorAutoProxyCreator:處理基於xml的aop
  • AnnotationAwareAspectJAutoProxyCreator:處理基於註解的aop

這三者的優先級爲AnnotationAwareAspectJAutoProxyCreator > AspectJAwareAdvisorAutoProxyCreator > InfrastructureAdvisorAutoProxyCreator,注入時,會判斷注入類的優先級,優先級高的最終會被注入到spring容器中。這樣就導致了一個問題:如果項目中同時開啓了aop(@EnableAspectJAutoProxy)與事務(@EnableTransactionManagement),那麼最終注入到容器的將是AnnotationAwareAspectJAutoProxyCreator,這也就是說,AnnotationAwareAspectJAutoProxyCreator也能處理事務! 這句話非常關鍵,它意味着事務的處理過程,實際上就包含在前面分析的aop的過程中了!

我們也來看看InfrastructureAdvisorAutoProxyCreator

// 繼承了 AbstractAdvisorAutoProxyCreator,這個類非常關鍵
public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {

    @Nullable
    private ConfigurableListableBeanFactory beanFactory;

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

    @Override
    protected boolean isEligibleAdvisorBean(String beanName) {
        return (this.beanFactory != null && 
                this.beanFactory.containsBeanDefinition(beanName) 
                &&  this.beanFactory.getBeanDefinition(beanName).getRole() 
                                == BeanDefinition.ROLE_INFRASTRUCTURE);
    }

}

InfrastructureAdvisorAutoProxyCreator 其實並沒有做什麼與aop相關的事,但它繼承了一個關鍵的類:AbstractAdvisorAutoProxyCreator,這個類可是大有來頭:

從繼承關係來看,這個類繼承了AbstractAutoProxyCreator,而 AbstractAutoProxyCreator 正是我們在- spring aop 之 AnnotationAwareAspectJAutoProxyCreator 分析(上)spring aop 之 AnnotationAwareAspectJAutoProxyCreator 分析(下)中重點分析的、代理對象的產生所在!

我們再來看下AnnotationAwareAspectJAutoProxyCreatorAspectJAwareAdvisorAutoProxyCreatorInfrastructureAdvisorAutoProxyCreator這三者的關係:

可以看到,AspectJAwareAdvisorAutoProxyCreatorInfrastructureAdvisorAutoProxyCreator都繼承了AbstractAdvisorAutoProxyCreatorAnnotationAwareAspectJAutoProxyCreator又繼承了AspectJAwareAdvisorAutoProxyCreator

通過以上分析,AutoProxyRegistrar 最終向spring容器註冊了InfrastructureAdvisorAutoProxyCreator(aop未啓用的情況下),如果啓用了aop,則會註冊AspectJAwareAdvisorAutoProxyCreator(基於xmlaop)或AnnotationAwareAspectJAutoProxyCreator(基於annotationaop)。

3.2 ProxyTransactionManagementConfiguration

接下來我們來看看ProxyTransactionManagementConfiguration類。名字上來看,這是個配置類:

@Configuration(proxyBeanMethods = false)
public class ProxyTransactionManagementConfiguration 
        extends AbstractTransactionManagementConfiguration {

    /**
     * 讀取Spring的 @Transactional 註解,並將相應的事務屬性公開給Spring的事務基礎結構
     */
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionAttributeSource transactionAttributeSource() {
        return new AnnotationTransactionAttributeSource();
    }

    /**
     * TransactionInterceptor繼承了Advice,這個類是個advice,用來處理事務的執行操作
     * @param transactionAttributeSource:來自於上面的 transactionAttributeSource() 方法
     */
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionInterceptor transactionInterceptor(
            TransactionAttributeSource transactionAttributeSource) {
        TransactionInterceptor interceptor = new TransactionInterceptor();
        // 設置事務屬性處理對象,就是用來處理 @Transactional 註解 的讀取
        interceptor.setTransactionAttributeSource(transactionAttributeSource);
        if (this.txManager != null) {
            interceptor.setTransactionManager(this.txManager);
        }
        return interceptor;
    }

    /**
     * 事務增強器.
     * @param transactionAttributeSource:來自於上面的 transactionAttributeSource() 方法
     * @param transactionInterceptor:來自於上面的 transactionInterceptor(...) 方法
     */
    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
            TransactionAttributeSource transactionAttributeSource,
            TransactionInterceptor transactionInterceptor) {
        BeanFactoryTransactionAttributeSourceAdvisor advisor 
                = new BeanFactoryTransactionAttributeSourceAdvisor();
        // 事務屬性類,用來保存 @Transactional 的屬性
        advisor.setTransactionAttributeSource(transactionAttributeSource);
        // 配置advice,在advice裏處理事務
        advisor.setAdvice(transactionInterceptor);
        if (this.enableTx != null) {
            advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
        }
        return advisor;
    }

}

可以看到這個類引入了一 些bean

  • transactionAttributeSource:類型爲AnnotationTransactionAttributeSource,用來解析 @Transactional 註解;
  • transactionInterceptor:類型爲TransactionInterceptorAdvice的子類,處理事務的邏輯在這個類裏;
  • transactionAdvisor:類型爲BeanFactoryTransactionAttributeSourceAdvisor,這是個Advisor,用來處理切面邏輯,內部集成了上面兩個對象:transactionAttributeSourcetransactionInterceptor

ProxyTransactionManagementConfiguration繼承了AbstractTransactionManagementConfiguration,而AbstractTransactionManagementConfiguration中也引入了一些bean

@Configuration
public abstract class AbstractTransactionManagementConfiguration implements ImportAware {

    @Nullable
    protected AnnotationAttributes enableTx;

    /**
     * 保存事務管理器
     */
    @Nullable
    protected TransactionManager txManager;

    /**
     * 來自於 ImportAware 接口的方法
     */
    @Override
    public void setImportMetadata(AnnotationMetadata importMetadata) {
        this.enableTx = AnnotationAttributes.fromMap(importMetadata
                .getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
        if (this.enableTx == null) {
            throw new IllegalArgumentException(
                    "@EnableTransactionManagement is not present on importing class " 
                    + importMetadata.getClassName());
        }
    }

    /**
     * 配置事務管理器.
     * 注入spring容器中所有的 TransactionManagementConfigurer 對象
     * TransactionManagementConfigurer就只有一個方法:
     *  TransactionManager annotationDrivenTransactionManager()
     * 這個方法用來返回一個事務管理器
     */
    @Autowired(required = false)
    void setConfigurers(Collection<TransactionManagementConfigurer> configurers) {
        if (CollectionUtils.isEmpty(configurers)) {
            return;
        }
        if (configurers.size() > 1) {
            throw new IllegalStateException("Only one TransactionManagementConfigurer may exist");
        }
        TransactionManagementConfigurer configurer = configurers.iterator().next();
        this.txManager = configurer.annotationDrivenTransactionManager();
    }

    /**
     * 處理事件監聽,用來處理 @TransactionalEventListener 註解的方法.
     */
    @Bean(name = TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public static TransactionalEventListenerFactory transactionalEventListenerFactory() {
        return new TransactionalEventListenerFactory();
    }

}
  • void setConfigurers(Collection<TransactionManagementConfigurer> configurers):注入TransactionManagementConfigurer對象,具體作用已在代碼註釋中說明;
  • TransactionalEventListenerFactory:類型爲TransactionalEventListenerFactory,用來處理事務事件(主要是@TransactionalEventListener 註解的方法,關於這部分的內容,本文就不展開了)。

好了,有了這些對象後,spring 就可以進行事務處理了,這些我們留到下篇文章再分析。

4. 總結

本文先是從兩個demo入手,示範瞭如果是由我們自己開發一個基於spring aop的事務管理功能是如何進行的,接着又用一個demo示範瞭如何使用spring提供的事務管理功能,然後就具體分析了spring事務啓用註解@EnableTransactionManagement的功能。

@EnableTransactionManagement是spring中用來啓用事務管理功能的,在AdviceModeproxy模式下,該註解向spring中引入了兩個類:AutoProxyRegistrarProxyTransactionManagementConfiguration,作用如下:

  • AutoProxyRegistraraop未啓用的情況下,會向spring容器中註冊InfrastructureAdvisorAutoProxyCreator;如果啓用了aop,則會註冊AspectJAwareAdvisorAutoProxyCreator(基於xmlaop)或AnnotationAwareAspectJAutoProxyCreator(基於annotationaop)。這三個類都是AbstractAdvisorAutoProxyCreator的子類,用來生成代理對象。

  • ProxyTransactionManagementConfiguration:這是一個配置類,通過帶有@Bean註解的方法向容器中引入了一系列的bean,用來處理事務邏輯,對這些bean,本文只需大概瞭解即可。

本文就先到這裏了,下篇文章繼續分析事務處理機制。


本文原文鏈接:https://my.oschina.net/funcy/blog/4773454 ,限於作者個人水平,文中難免有錯誤之處,歡迎指正!原創不易,商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

本系列的其他文章

【spring源碼分析】spring源碼分析系列文章

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