Spring源码分析之事务处理

前言

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对象的过程中会处理事务传播行为

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

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

分析总结

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

posted @ 2022-05-14 18:50  strongmore  阅读(37)  评论(0编辑  收藏  举报