【深入学习Spring】8——Spring事务管理源码分析

Spring AOP源码分析中我们分析了Spring AOP的工作原理,而Spring的事务管理是基于Spring AOP的。所以,搞懂了Spring AOP的工作流程后再来研究Spring的事务管理会更轻车熟路。

一、事务程序

先以一段事务管理的程序来引出后文。首先,在mysql中默认的test库中创建一张tbl_user表。

接下来写一段程序,用上事务管理。

下面是pom文件

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.26.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>4.3.26.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.3.26.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.3.26.RELEASE</version>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.0.4</version>
            <!--<scope>runtime</scope>-->
        </dependency>
    </dependencies>
View Code

然后是主程序

@EnableTransactionManagement
@ComponentScan("com.lp.test.tx")
@Configuration
public class TxConfig {

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");

        return dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource());
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

}

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    @Transactional
    public void insertUser() {
        userDao.insert();
        System.out.println("insert Success!");

        //int i = 1 / 0;
    }

}

@Repository
public class UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void insert() {
        String sql = "INSERT INTO tbl_user(name,age) VALUES(?,?)";

        String username = UUID.randomUUID().toString().substring(0, 5);
        jdbcTemplate.update(sql, username, 18);
    }

}

public class TxMain {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
        UserService userService = (UserService) context.getBean("userService");
        userService.insertUser();
    }

}
View Code

二、源码分析

下面开始源码跟踪和分析。要使用事务功能,首先需要在配置类上加上@EnableTransactionManagement注解,该注解是整个事务管理的开关,所以我们先从该注解入手来进行研究。首先查看一下该注解的声明

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

    boolean proxyTargetClass() default false;

    AdviceMode mode() default AdviceMode.PROXY;//默认为PROXY

    int order() default Ordered.LOWEST_PRECEDENCE;

}

发现该注解又通过@Import注解导入了一个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:
                return new String[] {
                        TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
            default:
                return null;
        }
    }

}

TransactionManagementConfigurationSelector继承了AdviceModeImportSelector,最终实现的是ImportSelector接口。在Spring容器中注册组件的几种方式总结中我们已经提到过ImportSelector搭配@Import可以用来向容器中导入组件。看看其中的selectImports方法,adviceMode有两种模式:PROXY和ASPECTJ。我们再回头看看@EnableTransactionManagement注解的声明,其中的AdviceMode属性默认值为PROXY,所以这里我们直接关注PROXY模式。也就是说默认的PROXY模式下,TransactionManagementConfigurationSelector又会导入两个组件:AutoProxyRegistrarProxyTransactionManagementConfiguration

这里直接或间接的导入了好几个组件,这里我画图来展示。

那我们就来开始研究这两个组件。

1.AutoProxyRegistrar

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");//获取注解属性中的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) {//PROXY模式
                    AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);//【注册组件】
                    if ((Boolean) proxyTargetClass) {
                        AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                        return;
                    }
                }
            }
        }
        if (!candidateFound && logger.isWarnEnabled()) {
            String name = getClass().getSimpleName();
            logger.warn(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));
        }
    }

}

AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
  -->registerAutoProxyCreatorIfNecessary()
    -->registerOrEscalateApcAsRequired()

    public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
        return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);//【InfrastructureAdvisorAutoProxyCreator】
    }

    private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

        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;
        }

        //注册组件,类型为方法参数中传入的InfrastructureAdvisorAutoProxyCreator
        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;
    }

调用registerOrEscalateApcAsRequired()时,会注册一个组件InfrastructureAdvisorAutoProxyCreator,那我们就来看看该类。下面是该类继承体系。现在再回头看看Spring源码系列7 - AOP中介绍的AnnotationAwareAspectJAutoProxyCreator类继承体系图,发现它们是从共同父类AbstractAutoProxyCreator一路继承下来。所以,我将AOP部分的AnnotationAwareAspectJAutoProxyCreator也一并画了进来,便于参照理解。

既然有了参照,我们理解起来应该就很容易了。InfrastructureAdvisorAutoProxyCreator自然也是一个后置处理器,其上层父类AbstractAutoProxyCreator实现了后置处理器中的回调方法postProcessAfterInitialization,该方法在执行时会调用wrapIfNecessary()对bean进行包装,这里的包装就是对Bean创建代理对象(增强)

2.ProxyTransactionManagementConfiguration

我们再来看另一个组件ProxyTransactionManagementConfiguration,它是一个配置类,通过该配置类会注册一个事务增强器BeanFactoryTransactionAttributeSourceAdvisor,而该事务增强器需要一个事务属性源和一个事务拦截器,所以配置类中也同时注册一个事务属性源和一个事务拦截器。

@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

    //事务增强器
    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        advisor.setTransactionAttributeSource(transactionAttributeSource());
        advisor.setAdvice(transactionInterceptor());
        advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
        return advisor;
    }

    //事务属性源
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionAttributeSource transactionAttributeSource() {
        return new AnnotationTransactionAttributeSource();//注解事务属性源,
    }

    //事务拦截器
    @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;
    }

}

先来看看这个注解事务属性源,进入构造方法就知道了原来它添加了事务注解解析器,在当前Spring环境下也就是添加了一个SpringTransactionAnnotationParser,该解析器就是用来解析事务属性的,也就是方法上的@Transaction注解中的属性

    public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
        this.publicMethodsOnly = publicMethodsOnly;
        this.annotationParsers = new LinkedHashSet<TransactionAnnotationParser>(4);
        this.annotationParsers.add(new SpringTransactionAnnotationParser());//Spring事务注解解析器
        if (jta12Present) {
            this.annotationParsers.add(new JtaTransactionAnnotationParser());//Jta
        }
        if (ejb3Present) {
            this.annotationParsers.add(new Ejb3TransactionAnnotationParser());Ejb
        }
    }

另外,通过声明就知道事务拦截器是一个方法拦截器,其作用就是在目标方法调用时会进行拦截,从而执行增强操作。在下面目标方法调用部分会进行讲解。

3.目标方法调用

当我们的程序中insertUser()方法被调用时,拦截器链就要大显身手了。事务管理本质上也是基于AOP的,前面我们写的程序使用的是Cglib动态代理方式,在Spring源码系列7 - AOP源码分析 中我们已经分析过Cglib代理方式下会使用拦截器DynamicAdvisedInterceptor(它是CglibAopproxy类的内部类)拦截目标方法的调用。

CglibAopProxy.DynamicAdvisedInterceptor.invoke()
  -->ReflectiveMethodInvocation.proceed() //执行拦截器链

之后的流程就基本上是一样的了。执行拦截器链时,拦截器链中只有一个事务拦截器。

我们来看看事务拦截器做了些什么操作。

    public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {

        ……

        @Override
        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, new InvocationCallback() {
                @Override
                public Object proceedWithInvocation() throws Throwable {
                    return invocation.proceed();
                }
            });
        }
        
        ……

    }

TransactionInterceptor#invoke
  -->TransactionAspectSupport#invokeWithinTransaction

    protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
            throws Throwable {

        //1.获取事务的属性
        // If the transaction attribute is null, the method is non-transactional.    
        final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
        //2.获取平台事务管理器
        final PlatformTransactionManager tm = determineTransactionManager(txAttr);
        final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

        if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
            //3.开启事务
            // Standard transaction demarcation with getTransaction and commit/rollback calls.
            TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

            Object retVal;
            try {
                //4.执行方法
                // 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) {
                //5.1如果发生异常,进行回滚
                // target invocation exception
                completeTransactionAfterThrowing(txInfo, ex);
                throw ex;
            }
            finally {
                cleanupTransactionInfo(txInfo);
            }
            //5.2.如果正常执行,提交事务
            commitTransactionAfterReturning(txInfo);
            return retVal;
        }

        else {
            final ThrowableHolder throwableHolder = new ThrowableHolder();

            // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
            try {
                Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
                        new TransactionCallback<Object>() {
                            @Override
                            public Object doInTransaction(TransactionStatus status) {
                                TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                                try {
                                    return invocation.proceedWithInvocation();
                                }
                                catch (Throwable ex) {
                                    if (txAttr.rollbackOn(ex)) {
                                        // A RuntimeException: will lead to a rollback.
                                        if (ex instanceof RuntimeException) {
                                            throw (RuntimeException) ex;
                                        }
                                        else {
                                            throw new ThrowableHolderException(ex);
                                        }
                                    }
                                    else {
                                        // A normal return value: will lead to a commit.
                                        throwableHolder.throwable = ex;
                                        return null;
                                    }
                                }
                                finally {
                                    cleanupTransactionInfo(txInfo);
                                }
                            }
                        });

                // Check result state: It might indicate a Throwable to rethrow.
                if (throwableHolder.throwable != null) {
                    throw throwableHolder.throwable;
                }
                return result;
            }
            catch (ThrowableHolderException ex) {
                throw ex.getCause();
            }
            catch (TransactionSystemException ex2) {
                if (throwableHolder.throwable != null) {
                    logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                    ex2.initApplicationException(throwableHolder.throwable);
                }
                throw ex2;
            }
            catch (Throwable ex2) {
                if (throwableHolder.throwable != null) {
                    logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                }
                throw ex2;
            }
        }
    }
    
    protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
        ……
        try {
            //拿到事务管理器,执行回滚
            txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
        }
        ……
    }

    protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
        ……
        //拿到事务管理器,提交事务
        txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
        ……        
    }

整个过程我们应该非常熟悉,获取了事务属性和平台事务管理器后,接下来要执行方法了,执行方法前如果有必要的话(开启了事务),则先开启事务。然后执行方法,如果方法正常执行,则提交事务。如果方法执行发生异常,则回滚事务。

 

总结

我们通过@EnableTransactionManagement注解打开事务管理的开关,该注解又通过@Import向容器中导入了一个TransactionManagementConfigurationSelector选择器组件,该选择器组件又会向容器中导入AutoProxyRegistrar和ProxyTransactionManagementConfiguration这两个组件。

  • AutoProxyRegistrar:这个组件又会向容器中注册另外一个组件InfrastructureAdvisorAutoProxyCreator,该组件是一个后置处理器,作用是在Bean创建完并初始化后进行包装操作,具体上就是对Bean创建代理对象(增强)。[回忆一下refresh中有一个注册后置处理器的步骤]
  • ProxyTransactionManagementConfiguration:这是一个配置类,通过该配置类会注册一个事务增强器BeanFactoryTransactionAttributeSourceAdvisor,一个事务属性源AnnotationTransactionAttributeSource和一个事务拦截器TransactionInterceptor 。

代理对象的方法在执行时,会走拦截器链,拦截器链中的事务拦截器会拦截到目标方法,拦截后就执行事务操作:(如有需要)开启事务,执行目标方法。正常执行完则提交事务,执行异常则回滚事务。

posted @ 2020-03-02 21:55  静水楼台/Java部落阁  阅读(798)  评论(0编辑  收藏  举报