Spring源碼分析:聲明式事務梳理

使用註解方式簡單模擬事務

樣例

說明

  1. 數據源採用c3p0
  2. 採用JdbcTemplate持久化
  3. 採用Spring事務註解

環境搭建

POM依賴

<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>
<!-- spring提供的jdbcTemplate模塊 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>4.3.12.RELEASE</version>
</dependency>
<!-- mysql連接驅動包 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.44</version>
    <scope>runtime</scope>
</dependency>
<!-- AOP -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.12.RELEASE</version>
</dependency>

配置類

/**
 * description:聲明式事務配置類,其中@EnableTransactionManagement
 * 必定要開啓。
 * @author 70KG
 */
@Configuration
@ComponentScan("com.nmys.story.springCore.springaop.tx_sample")
@EnableTransactionManagement // -- 開啓基於註解的事務管理
public class TxConfig {

    // -- 配置數據源
    @Bean
    public DataSource dataSource() throws Exception {
        ComboPooledDataSource pool = new ComboPooledDataSource();
        pool.setUser("root");
        pool.setPassword("root");
        pool.setDriverClass("com.mysql.jdbc.Driver");
        pool.setJdbcUrl("jdbc:mysql://localhost:3306/usthe?useSSL=false");
        return pool;
    }

    // -- 加入模板
    @Bean
    public JdbcTemplate jdbcTemplate() throws Exception {
        JdbcTemplate template = new JdbcTemplate(dataSource());
        return template;
    }

    // -- 配置事務管理器,它纔是用來提交回滾事務的主導者
    @Bean
    public DataSourceTransactionManager txManager() throws Exception {
        DataSourceTransactionManager tx = new DataSourceTransactionManager(dataSource());
        return tx;
    }

}

業務類

/**
 * description
 * @author 70KG
 */
@Service
public class TxService {

    @Autowired
    private TxDao txDao;

    public void insertLog(){
        txDao.insertSth();
    }

}

<!--more-->java

/**
 * description
 * @author 70KG
 */
@Repository
public class TxDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    // @Transactional僅代表它是一個事務方法,開啓事務僅有註解是不夠的,還須要配置事務管理器
    @Transactional
    public void insertSth() {
        String sql = "INSERT into sys_log (username) VALUES(?);";
        jdbcTemplate.update(sql, "lisi");
        System.out.println("------>插入成功");
        int i = 10/0;
    }
}

測試類

/**
 * description
 * @author 70KG
 */
public class Test01 {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(TxConfig.class);
        TxService bean = app.getBean(TxService.class);
        bean.insertLog();
    }

}

測試結果

測試結果確定是能正常運行的,下面試着跟一下源碼。mysql

源碼分析

  1. 當容器開始啓動運行的時候就會找到@EnableTransactionManagement註解
  2. 進入註解,發現它使用@Import(TransactionManagementConfigurationSelector.class)向容器中注入了這個類
  3. 跟進TransactionManagementConfigurationSelector,發現它最終實現的是ImportSelector接口,這個接口能夠向IOC容器中以Bean的全類名的方式注入Bean。

源碼以下,AdviceMode在註解@EnableTransactionManagement默認就是PROXY,可見它向容器中注入了兩個類,分別是AutoProxyRegistrar和ProxyTransactionManagementConfiguration。面試

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:
                return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
            default:
                return null;
        }
    }
}

AutoProxyRegistrar

  1. AutoProxyRegistrar翻譯過來:自動代理註冊器。進入AutoProxyRegistrar類,截取部分源碼,以下:
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    boolean candidateFound = false;
    Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
    for (String annoType : annoTypes) {
        AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
        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) {
                // -- 前面的代碼主要是獲取註解類型,註解信息等等。
                // -- 主要是這個地方,若是必要的話,就向容器中註冊一個自動代理建立器。
                AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                if ((Boolean) proxyTargetClass) {
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                    return;
                }
            }
        }
    }
    ...........
  1. 進入registerAutoProxyCreatorIfNecessary(registry),AopConfigUtils類中,源碼以下:
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry,
        @Nullable Object source) {
    // -- 斷言
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    // 先判斷有沒有org.springframework.aop.config.internalAutoProxyCreator
    // 首次進來,確定沒有
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        return null;
    }
    // -- 將cls也就是InfrastructureAdvisorAutoProxyCreator包裝成RootBeanDefinition
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // -- 將beanDefinition註冊到IOC容器中,Bean的
    // -- 名字就叫org.springframework.aop.config.internalAutoProxyCreator
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}
  1. 向容器中注入了InfrastructureAdvisorAutoProxyCreator,進入此類,發現父類是AbstractAdvisorAutoProxyCreator,AbstractAdvisorAutoProxyCreator的父類是AbstractAutoProxyCreator,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) throws BeansException {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

給容器中注入InfrastructureAdvisorAutoProxyCreator的主要做用就是,利用後置處理器機制在對象建立之後,對對象進行包裝,返回一個代理對象(加強器),代理對象執行方法,利用攔截器鏈進行調用。spring

ProxyTransactionManagementConfiguration

  1. 進入ProxyTransactionManagementConfiguration,部分源碼以下:
// -- 向容器中注入名字爲TRANSACTION_ADVISOR_BEAN_NAME的切面
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
    BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        // -- 向切面中注入註解解析器,專門來解析事務註解的
    advisor.setTransactionAttributeSource(transactionAttributeSource());
        // -- 向切面中注入事務的攔截器,專門來攔截方法,包括事務的提交以及回滾操做
    advisor.setAdvice(transactionInterceptor());
    if (this.enableTx != null) {
        advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
    }
    return advisor;
}
  1. 進入transactionAttributeSource()註解解析器,源碼以下:
public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
    this.publicMethodsOnly = publicMethodsOnly;
    this.annotationParsers = new LinkedHashSet<>(2);
        // -- Spring註解的解析器
    this.annotationParsers.add(new SpringTransactionAnnotationParser());
    if (jta12Present) {
                // -- jta的解析器
        this.annotationParsers.add(new JtaTransactionAnnotationParser());
    }
    if (ejb3Present) {
                // -- ejb的解析器
        this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
    }
}
  1. 進入SpringTransactionAnnotationParser(),部分源碼以下:
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
    RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
    Propagation propagation = attributes.getEnum("propagation");
    rbta.setPropagationBehavior(propagation.value());
    Isolation isolation = attributes.getEnum("isolation");
    rbta.setIsolationLevel(isolation.value());
    rbta.setTimeout(attributes.getNumber("timeout").intValue());
    rbta.setReadOnly(attributes.getBoolean("readOnly"));
    rbta.setQualifier(attributes.getString("value"));
    ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<>();
    Class<?>[] rbf = attributes.getClassArray("rollbackFor");
    for (Class<?> rbRule : rbf) {

會發現@Transactional中的各類屬性都在這裏,這樣,註解解析器就分析完了sql

  1. 再來看事務的攔截器,分析事務是如何回滾和提交的,進入transactionInterceptor()
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor() {
        // -- 事務的攔截器
    TransactionInterceptor interceptor = new TransactionInterceptor();
    interceptor.setTransactionAttributeSource(transactionAttributeSource());
    if (this.txManager != null) {
                // -- 將事務管理器設置進去,爲了事務的提交和回滾操做
        interceptor.setTransactionManager(this.txManager);
    }
    return interceptor;
}

TransactionInterceptor 是一個實現了MethodInterceptor接口的類,標誌着TransactionInterceptor是一個方法攔截器,進入它的invoke()方法app

@Override
@Nullable
public Object invoke(final MethodInvocation invocation) throws Throwable {
    // Work out the target class: may be {@code null}.
    // The TransactionAttributeSource should be passed the target class
    // as well as the method, which may be from an interface.
    Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
    // Adapt to TransactionAspectSupport's invokeWithinTransaction...
    return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}

進入invokeWithinTransaction()ide

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
        final InvocationCallback invocation) throws Throwable {
    // If the transaction attribute is null, the method is non-transactional.
    TransactionAttributeSource tas = getTransactionAttributeSource();
        // -- 拿到事務註解信息包括事務的qualifier和rollback信息
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
        // -- 獲取事務管理器
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
        // -- 事務鏈接點的定義信息
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // 建立並開啓事務
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
        Object retVal = null;
        try {
            // This is an around advice: Invoke the next interceptor in the chain.
            // This will normally result in a target object being invoked.
                        // -- 調用目標方法
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            // target invocation exception
                        // -- 出現異常了,獲取事務管理器,進行事務的回滾
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            cleanupTransactionInfo(txInfo);
        }
                // -- 未出現異常,也獲取事務管理器則進行事務的提交
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }
    .........

總結

Spring事務源碼梳理
  1. 經過註解@EnableTransactionManagement中的@Import(TransactionManagementConfigurationSelector.class)給容器中導入了兩個組件,分別是:AutoProxyRegistrar和ProxyTransactionManagementConfiguration
  2. AutoProxyRegistrar:它是一個後置處理器,給容器中註冊一個InfrastructureAdvisorAutoProxyCreator,InfrastructureAdvisorAutoProxyCreator利用後置處理器機制在對象建立之後,對對象進行包裝,返回一個代理對象(加強器),代理對象執行方法,利用攔截器鏈進行調用。
  3. ProxyTransactionManagementConfiguration:給容器中註冊事務加強器
  • 事務加強器要用事務註解信息:AnnotationTransactionAttributeSource來解析事務註解
  • 事務攔截器中:transactionInterceptor(),它是一個TransactionInterceptor(保存了事務屬性信息和事務管理器),而TransactionInterceptor是一個MethodInterceptor,在目標方法執行的時候,執行攔截器鏈,事務攔截器(首先獲取事務相關的屬性,再獲取PlatformTransactionManager,若是沒有指定任何transactionManager,最終會從容器中按照類型獲取一個PlatformTransactionManager,最後執行目標方法,若是異常,便獲取到事務管理器進行回滾,若是正常,一樣拿到事務管理器提交事務。
相關文章
相關標籤/搜索