Spring 事务
1. 使用事务
Spring 支持两种类型的事务,声明式事务和编程式事务。
- 编程式事务:类似于 Jdbc 事务的写法,需要将事务的代码嵌入到业务逻辑中,这样代码的耦合度较高。
- 声明式事务:通过AOP的思想能够有效的将事务和业务逻辑代码解耦,因此在实际开发中,声明式事务得到了广泛的应用,而编程式事务则较少使用
@Service public class DemoService { @Autowired DataSourceTransactionManager txManager; // 1. 编程式事务 public void saveDb() { // 设置事务属性 DefaultTransactionDefinition definition = new DefaultTransactionDefinition(); // 开启一个事务 TransactionStatus status = txManager.getTransaction(definition); try { // 操作数据库 // 提交事务 txManager.commit(status); } catch (DataAccessException e) { e.printStackTrace(); // 回滚事务 txManager.rollback(status); } } // 2.声明式事务 @Transactional public void saveDb2() { // 操作数据库 } }
2. Spring 事务核心代码
Spring对事务的支持提供了3个核心基础类,分别如下:
- PlatformTransactionManager
- TransactionDefinition
- TransactionStatus
2.1 PlatformTransactionManager
核心逻辑处理。根据ORM框架的不同,事务管理器的实现类也不同
- JDBC 和 mybatis的是 DataSourceTransactionManager
- Hibernate HibernateTransactionManager
- JPA JpaTransactionManager
// 定义事务管理器
@Bean(name = "dataSourceTx")
public DataSourceTransactionManager dataSourceTx(@Qualifier("dataSource") DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
提供的能力如下:
public interface PlatformTransactionManager extends TransactionManager {
// 1.开启事务
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
// 2.提交事务
void commit(TransactionStatus status) throws TransactionException;
// 3.回滚事务
void rollback(TransactionStatus status) throws TransactionException;
}
2.2 TransactionDefinition
定义当前事务的属性。比如,隔离级别、传播规则、回滚策略、超时时间、是否只读
public interface TransactionDefinition {
// 传播规则
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
// 隔离级别
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
// 超时时间,默认-1,不超时
int TIMEOUT_DEFAULT = -1;
// 获取事务的传播性
default int getPropagationBehavior() {
return PROPAGATION_REQUIRED;
}
// 获取事务的隔离级别
default int getIsolationLevel() {
return ISOLATION_DEFAULT;
}
// 获取事务的超时时间
default int getTimeout() {
return TIMEOUT_DEFAULT;
}
// 获取事务是否是只读事务
default boolean isReadOnly() {
return false;
}
// 获取事务的名称
default String getName() {
return null;
}
static TransactionDefinition withDefaults() {
return StaticTransactionDefinition.INSTANCE;
}
}
2.3 TransactionStatus
事务的状态,可以理解为事务本身的属性
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
/**
* 是否有Savepoint Savepoint是当事务回滚时需要恢复的状态
*/
boolean hasSavepoint();
/**
* flush()操作和底层数据源有关,并非强制所有数据源都要支持
*/
void flush();
/**
* 是否是新事务(或是其他事务的一部分)
*/
boolean isNewTransaction();
/**
* 设置rollback-only 表示之后需要回滚
*/
void setRollbackOnly();
/**
* 是否rollback-only
*/
boolean isRollbackOnly();
/**
* 判断该事务已经完成
*/
boolean isCompleted();
/**
* 创建一个Savepoint
*/
Object createSavepoint() throws TransactionException;
/**
* 回滚到指定Savepoint
*/
void rollbackToSavepoint(Object savepoint) throws TransactionException;
/**
* 释放Savepoint 当事务完成后,事务管理器基本上自动释放该事务所有的savepoint
*/
void releaseSavepoint(Object savepoint) throws TransactionException;
}
3.事务实现原理
3.1 开启事务
有两种方式
- 使用注解 @EnableTransactionManagement 声明开启事务。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
// 用来表示默认使用JDK Dynamic Proxy还是CGLIB Proxy
boolean proxyTargetClass() default false;
// 表示以Proxy-based方式实现AOP还是以Weaving-based方式实现AOP
AdviceMode mode() default AdviceMode.PROXY;
// 顺序
int order() default Ordered.LOWEST_PRECEDENCE;
}
- 通过在 DataSource 配置的时候,通过@Bean 配置 DataSourceTransactionManager。
在spring boot 中,我们添加了数据库配置、创建了 DataSourceTransactionManager 后,会触发spring.factories 配置类的加载条件。
// 配置1。自动配置事务管理器
// 触发条件:使用了jdbcTemplate或者配置了数据库事务。
// 触发事件:自动将环境中的配置加载到 DataSourceProperties 中
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration {
}
// 配置2:事务自动配置
// 触发条件:1. 配置了数据库事务。 2.在等待 AutoConfigureAfter 中指定的配置加载完成后才开始加载
// 触发事件:自动加载事务的配置
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class })
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionAutoConfiguration {
// 内部类,配置3:配置事务的代理方式以及注册事务的注解 EnableTransactionManagement
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(TransactionManager.class)
@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
public static class EnableTransactionManagementConfiguration {
@Configuration(proxyBeanMethods = false)
@EnableTransactionManagement(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableTransactionManagement(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
public static class CglibAutoProxyConfiguration {
}
}
}
3.2 注册事务配置器
通过@Import引入了另一个配置TransactionManagentConfigurationSelector。
在Spring启动的时候,向容器注册BeanDefinition的。注册的触发代码是 ConfigurationClasspathPostProcessor 调用 ConfigurationClassParser 触发。
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[] {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);
}
}
我们看到,事务的代理模式有2种
- PROXY。 JDK proxy-based advice。注册了2个配置类。
- AutoProxyRegistrar。向容器注册了 InfrastructureAdvisorAutoProxyCreator。这是 AbstractAdvisorAutoProxyCreator 的子类,BeanPostProcessor 实现类。用于创建代理对象。
- ProxyTransactionManagementConfiguration,是个配置类
- TransactionalEventListenerFactory
- BeanFactoryTransactionAttributeSourceAdvisor。包括 TransactionAttributeSourcePointcut(filter和matches之类的方法,用来匹配被代理类) 和 TransactionInterceptor(事务逻辑核心,aop的拦截器) 两个子类。
- TransactionAttributeSource。TransactionDefinition 的扩展,增加了isCandidateClass()的方法(可以用来帮助Pointcut匹配)。实现类时 AnnotationTransactionAttributeSource
- TransactionInterceptor。我们事务实现逻辑的切面
- ASPECTJ。 AspectJ weaving-based advice。是一种 编译期织入 实现的方式。比较少用
3.3 启动流程
与aop的启动流程一致。 InfrastructureAdvisorAutoProxyCreator 的作用和 AnnotationAwareAspectJAutoProxyCreator 是一样的。
public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {
@Nullable
private ConfigurableListableBeanFactory beanFactory;
@Override
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.initBeanFactory(beanFactory);
this.beanFactory = beanFactory;
}
//在Spring容器中获取Advisor的Bean,然后判断是否是内部的bean对象。
@Override
protected boolean isEligibleAdvisorBean(String beanName) {
return (this.beanFactory != null && this.beanFactory.containsBeanDefinition(beanName) &&
this.beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE);
}
}
3.4 使用事务
当我们调用开始事务的方法时,本质是调用代理对象。代理对象 调用 aop拦截器的 invoke方法TransactionInterceptor的核心代码如下
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable {
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final TransactionManager tm = determineTransactionManager(txAttr);
//省略部分代码
//获取事物管理器
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// 打开事务(内部就是getTransactionStatus的过程)
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// 执行业务逻辑 invocation.proceedWithInvocation();
} catch (Throwable ex) {
// 异常回滚
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
} finally {
cleanupTransactionInfo(txInfo);
}
//省略部分代码
//提交事物
commitTransactionAfterReturning(txInfo);
return retVal;
}
}
事务实现原理: https://www.cnblogs.com/insaneXs/p/13638034.html
4.事务使用规范
【没有事务】
- 必须使用Spring管理的bean,且使用@Transactional注解声明
- DataSource必须开启事务管理器,否则事务都是自动提交不开启事务。
- 数据库必须支持事务。
【事务失效】
- 不要在接口上声明@Transactional ,而要在具体类的方法上使用 @Transactional 注解,否则注解可能无效
- 调用开启事务的对象的时候,必须调用代理对象。所有对象内部方法间的调用不走代理,事务就不生效。
- 方法中开启新的线程,因为事务是基于ThreadLocal实现的,所以这个线程无法继承父线程的事务。
- 基于接口动态代理的 AOP 事务(JdkProxy),方法必须是public,不能是static,
- 基于CGLib 字节码动态代理的方案是通过扩展被增强类,动态创建其子类的方式进行 AOP 增强植入的。 由于使用 final、static、private 修饰符的方法都不能被子类覆盖,这些方法将无法实施 AOP 增强。 所以方法不能是private,不能是final和static。
【事务存在,但回滚失败】
- 异常没有往外抛出,自己捕获异常
- 抛出受检异常。正常只有抛出运行时异常才会回滚,比如runtimeException和NullPointerException。 所以我们可以自己设置允许回滚的类:@Transactional(rollbackFor = FileNotFoundException.class)
5.声明式事务
5.1.隔离级别
org.springframework.transaction.annotation.Isolation 枚举类中定义了五个表示隔离级别的值:
使用方式 : @Transactional(isolation = Isolation.DEFAULT)
public enum Isolation {
DEFAULT(-1),
READ_UNCOMMITTED(1),
READ_COMMITTED(2),
REPEATABLE_READ(4),
SERIALIZABLE(8);
}
5.2.传播级别
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。
org.springframework.transaction.annotation.Propagation枚举类中定义了7个表示传播行为的枚举值:
public enum Propagation {
REQUIRED(0),
SUPPORTS(1),
MANDATORY(2),
REQUIRES_NEW(3),
NOT_SUPPORTED(4),
NEVER(5),
NESTED(6);
}
- REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
- NESTED:如果当前存在事务,则创建一个子事务作为当前事务的嵌套事务来运行,最终子事务和父事务一起提交;如果当前没有事务,则该取值等价于REQUIRED。
指定方法:通过使用propagation属性设置,例如: @Transactional(propagation = Propagation.REQUIRED)
5.3.事务回滚
5.4.超时设置
5.5.TransactionSynchronizationManager
https://blog.csdn.net/luzhensmart/article/details/89708881
TransactionSynchronizationManager:维护事务状态。在这个类中有一系列的ThreadLocal类型的类变量,它们就负责存储当前线程中的事务数据。
TransactionSynchronization接口定义了一系列的回调方法,对应一个事务执行的不同阶段:挂起、恢复、flush、提交(前、后)、完成(事务成功或失败)等。
@Transactional
public void save3() {
// 1. 数据库操作
// doSomething
// 2. 判断是否开启了事务。 将操作注册到"afterCommit"阶段
// 在事务提交之后,立即执行注册的方法。且这个方法时无法开启事务的
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// 做些跟事务没有关系的事情 比如。发送mq
}
});
}
}
5.6.TransactionAspectSupport
在开启的事务中,手动管理事务
@Transactional// 1.开启事务
@DataSourceSelector(value = DynamicDataSourceEnum.MASTER)
public void save() {
User AA = new User(1L, "aa", "aa", "aa", 1, null, null);
User BB = new User(2L, "bb", "bb", "bb", 1, null, null);
// 数据库操作A
this.userMapper.save(AA);
Object savePoint = null;
try {
// 2. 手动设置一个事务的保存点
savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
// 数据库操作B
this.userMapper.save(BB);
// 3.手动回滚事务到保存点
TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);
} catch (Exception e) {
e.printStackTrace();
}
// 这个时候,数据库操作A成功,B回滚
}
@Transactional
@DataSourceSelector(value = DynamicDataSourceEnum.MASTER)
public void save2() {
User AA = new User(1L, "aa", "aa", "aa", 1, null, null);
User BB = new User(2L, "bb", "bb", "bb", 1, null, null);
try {
// 数据库操作A
this.userMapper.save(AA);
// 数据库操作B
this.userMapper.save(BB);
// 之前回滚全部事务
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
} catch (Exception e) {
e.printStackTrace();
}
// 这个时候,上面的两个操作都失败的
}
- 本文作者:zhangxue
- 本文链接:https://lucksnow1989.github.io/article/9.开源/spring/5.Spring 事务.html
- 版权声明:本文为本人原创文章,遵循 CC 4.0 BY-NC-SA版权协议,转载请附上原文出处链接和本声明。