Spring源码分析之事务处理(Transaction processing of spring source code analysis)

前言

Spring对事务的支持依赖于SpringAOP的实现。

简单使用

create table test_db.tb_user(
    u_id   int auto_increment primary key, -- 主键自增
    u_name varchar(20) null, -- 用户名
    u_age  int         null  -- 年龄
)

在MYSQL数据库创建一个用户表

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User {

  private Integer uid;
  private String uname;
  private Integer age;
}

定义一个实体类,对应数据库的用户表

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.transaction.annotation.Transactional;

public class UserService {

  @Autowired
  private JdbcTemplate jdbcTemplate;

  @Transactional
  public void saveUser(User user) {
    String sql = "insert into tb_user(u_name, u_age) VALUES (?,?)";
    jdbcTemplate.update(sql, user.getUname(), user.getAge());
  }

  public List<User> queryUserList() {
    String sql = "select * from tb_user";
    RowMapper<User> rowMapper = new RowMapper<>() {
      @Override
      public User mapRow(ResultSet rs, int rowNum) throws SQLException {
        int uid = rs.getInt("u_id");
        String uname = rs.getString("u_name");
        int age = rs.getInt("u_age");
        return new User(uid, uname, age);
      }
    };
    return jdbcTemplate.query(sql, rowMapper);
  }
}

创建用户业务类,查询用户列表,新增用户记录。使用@Transactional注解表示该方法开启事务控制。

import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
public class BeanConfig {
  @Bean("userService")
  public UserService userService() {
    return new UserService();
  }
  @Bean("platformTransactionManager")
  public PlatformTransactionManager platformTransactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
  }
  @Bean("dataSource")
  public DataSource dataSource() {
    HikariDataSource dataSource = new HikariDataSource();
    dataSource.setJdbcUrl("jdbc:mysql://ip:3306/test_db");
    dataSource.setUsername("xxx");
    dataSource.setPassword("xxx");
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    return dataSource;
  }
  @Bean("jdbcTemplate")
  public JdbcTemplate jdbcTemplate(DataSource dataSource) {
    return new JdbcTemplate(dataSource);
  }
}

配置数据库数据源,事务管理器等Bean。@EnableTransactionManagement开启事务管理。

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestTransactional {

  public static void main(String[] args) {
    ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
    UserService userService = (UserService) context.getBean("userService");
    System.out.println(userService.queryUserList());
    User user = new User();
    user.setUname("小李");
    user.setAge(30);
    userService.saveUser(user);
    System.out.println(userService.queryUserList());
  }
}

保存用户,查询用户,都可以正常工作。

原理分析

关于如何解析@Configuration注解,可以查看Spring源码分析之@Configuration注解的处理。
我们在它的基础上可以知道,开启事务的核心在于@EnableTransactionManagement注解,它会导入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()};
                        //使用AspectJ,用的不多,先不用管
			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);
	}

}

会导入两个配置类

  • AutoProxyRegistrar:
    AutoProxyRegistrar是一个ImportBeanDefinitionRegistrar,会注册InfrastructureAdvisorAutoProxyCreator类,
    和开启AOP的AnnotationAwareAspectJAutoProxyCreator类似,都是BeanPostProcessor,在创建Bean的过程中根据条件判断是否创建代理对象。
    如果同时开启了事务和AOP,就是说同时注册InfrastructureAdvisorAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator,会使用后一个(优先级更高)。
  • ProxyTransactionManagementConfiguration:
    这是一个配置类,开启对@Transactional注解的拦截

关于AspectJ,可以查看AspectJ 使用介绍。

核心在于ProxyTransactionManagementConfiguration

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

        //声明事务管理的Advisor,在包含@Transactional的方法上应用事务管理
	@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;
	}

        //内部使用SpringTransactionAnnotationParser来解析@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;
	}

}

配置了一个Advisor,看过Spring源码分析之AOP使用和分析可以知道,
Advisor(通知器)包含Advice(通知)和Pointcut(切入点),这里的Pointcut就是类或方法包含@Transactional,Advice就是事务管理。
继续分析TransactionInterceptor,它是一个MethodInterceptor,也是Advice,处理实际的事务控制

@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
		//获取业务对象的Class,这里就是UserService
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
		//执行事务控制
		return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
	}

进入父类TransactionAspectSupport的invokeWithinTransaction()方法

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

		TransactionAttributeSource tas = getTransactionAttributeSource();
		//如果获取到的值为null,说明该方法不包含@Transactional注解
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
                //从容器中获取类型为TransactionManager的Bean对象,这里就是我们配置的DataSourceTransactionManager
		final TransactionManager tm = determineTransactionManager(txAttr);
                //转换为PlatformTransactionManager类型
		PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
                //当前方法的全路径,类路径.method
		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

		if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
			//创建TransactionInfo,其中包含一个数据库连接,且关闭了事务的自动提交
			TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

			Object retVal;
			try {
				//执行业务方法,这里就是UserService的saveUser()方法
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				//处理事务回滚,会判断当前异常和@Transactional注解配置的rollback是否匹配
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				cleanupTransactionInfo(txInfo);
			}
                        //事务提交
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}
		else {
		}
	}

创建TransactionInfo对象的过程中会处理事务传播行为

  • TransactionDefinition.PROPAGATION_REQUIRED,
    @Transactional注解默认行为。如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW,
    创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,都会新开启自己的事务,且开启的事务相互独立,互不干扰。
  • TransactionDefinition.PROPAGATION_NESTED,
    如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则创建一个新的事务。
  • TransactionDefinition.PROPAGATION_MANDATORY
    如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_SUPPORTS
    如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED
    以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER
    以非事务方式运行,如果当前存在事务,则抛出异常。

TransactionSynchronizationManager内部通过ThreadLocal来保存之前的事务。

分析总结

通过拦截包含@Transactional的类和方法,通过TransactionManager(事务管理器)来处理数据库连接的提交和回滚。

————————

preface

Spring’s support for transactions depends on the implementation of spring AOP.

Simple use

create table test_db.tb_user(
    u_id   int auto_increment primary key, -- 主键自增
    u_name varchar(20) null, -- 用户名
    u_age  int         null  -- 年龄
)

Create a user table in MySQL database

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User {

  private Integer uid;
  private String uname;
  private Integer age;
}

Define an entity class corresponding to the user table of the database

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.transaction.annotation.Transactional;

public class UserService {

  @Autowired
  private JdbcTemplate jdbcTemplate;

  @Transactional
  public void saveUser(User user) {
    String sql = "insert into tb_user(u_name, u_age) VALUES (?,?)";
    jdbcTemplate.update(sql, user.getUname(), user.getAge());
  }

  public List<User> queryUserList() {
    String sql = "select * from tb_user";
    RowMapper<User> rowMapper = new RowMapper<>() {
      @Override
      public User mapRow(ResultSet rs, int rowNum) throws SQLException {
        int uid = rs.getInt("u_id");
        String uname = rs.getString("u_name");
        int age = rs.getInt("u_age");
        return new User(uid, uname, age);
      }
    };
    return jdbcTemplate.query(sql, rowMapper);
  }
}

Create user business classes, query user lists, and add user records. Use the @ transactional annotation to indicate that this method turns on transaction control.

import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
public class BeanConfig {
  @Bean("userService")
  public UserService userService() {
    return new UserService();
  }
  @Bean("platformTransactionManager")
  public PlatformTransactionManager platformTransactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
  }
  @Bean("dataSource")
  public DataSource dataSource() {
    HikariDataSource dataSource = new HikariDataSource();
    dataSource.setJdbcUrl("jdbc:mysql://ip:3306/test_db");
    dataSource.setUsername("xxx");
    dataSource.setPassword("xxx");
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    return dataSource;
  }
  @Bean("jdbcTemplate")
  public JdbcTemplate jdbcTemplate(DataSource dataSource) {
    return new JdbcTemplate(dataSource);
  }
}

Configure beans such as database, data source and transaction manager@ Enable transaction management enables transaction management.

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestTransactional {

  public static void main(String[] args) {
    ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
    UserService userService = (UserService) context.getBean("userService");
    System.out.println(userService.queryUserList());
    User user = new User();
    user.setUname("小李");
    user.setAge(30);
    userService.saveUser(user);
    System.out.println(userService.queryUserList());
  }
}

Save users and query users can work normally.

Principle analysis

For how to parse the @ configuration annotation, you can see the processing of the @ configuration annotation in spring source code analysis.
Based on it, we can know that the core of starting a transaction is the @ enabletransactionmanagement annotation, which will import the transaction management configurationselector configuration.

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()};
                        //使用AspectJ,用的不多,先不用管
			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);
	}

}

Two configuration classes will be imported

  • AutoProxyRegistrar:
    AutoProxyRegistrar是一个ImportBeanDefinitionRegistrar,会注册InfrastructureAdvisorAutoProxyCreator类,
    和开启AOP的AnnotationAwareAspectJAutoProxyCreator类似,都是BeanPostProcessor,在创建Bean的过程中根据条件判断是否创建代理对象。
    如果同时开启了事务和AOP,就是说同时注册InfrastructureAdvisorAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator,会使用后一个(优先级更高)。
  • ProxyTransactionManagementConfiguration:
    这是一个配置类,开启对@Transactional注解的拦截

For AspectJ, you can view the introduction of AspectJ.

核心在于ProxyTransactionManagementConfiguration

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

        //声明事务管理的Advisor,在包含@Transactional的方法上应用事务管理
	@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;
	}

        //内部使用SpringTransactionAnnotationParser来解析@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;
	}

}

An advisor is configured. You can know from the AOP use and analysis of spring source code analysis,
The pointcut or actioncut class of advisor is the pointcut that contains the notification and transaction manager.
Continue to analyze the transactioninterceptor, which is a method interceptor and advice to handle the actual transaction control

@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
		//获取业务对象的Class,这里就是UserService
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
		//执行事务控制
		return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
	}

进入父类TransactionAspectSupport的invokeWithinTransaction()方法

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

		TransactionAttributeSource tas = getTransactionAttributeSource();
		//如果获取到的值为null,说明该方法不包含@Transactional注解
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
                //从容器中获取类型为TransactionManager的Bean对象,这里就是我们配置的DataSourceTransactionManager
		final TransactionManager tm = determineTransactionManager(txAttr);
                //转换为PlatformTransactionManager类型
		PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
                //当前方法的全路径,类路径.method
		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

		if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
			//创建TransactionInfo,其中包含一个数据库连接,且关闭了事务的自动提交
			TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

			Object retVal;
			try {
				//执行业务方法,这里就是UserService的saveUser()方法
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				//处理事务回滚,会判断当前异常和@Transactional注解配置的rollback是否匹配
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				cleanupTransactionInfo(txInfo);
			}
                        //事务提交
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}
		else {
		}
	}

Transaction propagation behavior is handled during the creation of transactioninfo objects

  • TransactionDefinition.PROPAGATION_REQUIRED,
    @Transactional注解默认行为。如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • TransactionDefinition. PROPAGATION_ REQUIRES_ NEW,
    Create a new transaction. If there is a current transaction, suspend the current transaction. In other words, no matter whether the external method starts the transaction or not, it will start its own transaction, and the opened transactions are independent of each other and do not interfere with each other.
  • TransactionDefinition. PROPAGATION_ NESTED,
    If a transaction currently exists, create a transaction to run as a nested transaction of the current transaction; If there is no current transaction, a new transaction is created.
  • TransactionDefinition.PROPAGATION_MANDATORY
    如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • TransactionDefinition. PROPAGATION_ SUPPORTS
    If a transaction currently exists, join the transaction; If there are currently no transactions, continue to run in a non transactional manner.
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED
    以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER
    以非事务方式运行,如果当前存在事务,则抛出异常。

TransactionSynchronizationManager内部通过ThreadLocal来保存之前的事务。

Analysis and summary

Intercept the classes and methods containing @ transactional, and handle the submission and rollback of database connections through the transaction manager.