现如今,对于一个Java程序猿来说,如果说不会用甚至不知道Spring,那肯定不是一个合格的Java程序猿。大家都知道,Spring最主要的俩大模块,分别是IoC和AOP。而今天主要讲的是Spring中的事务@Transactional注解。
Sping事务的传播机制
名称 | 解释 |
---|---|
TransactionDefinition.PROPAGATION_REQUIRED | 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。 |
TransactionDefinition.PROPAGATION_REQUIRES_NEW | 创建一个新的事务,如果当前存在事务,则把当前事务挂起。 |
TransactionDefinition.PROPAGATION_SUPPORTS | 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。 |
TransactionDefinition.PROPAGATION_NOT_SUPPORTED | 以非事务方式运行,如果当前存在事务,则把当前事务挂起。 |
TransactionDefinition.PROPAGATION_NEVER | 以非事务方式运行,如果当前存在事务,则抛出异常。 |
TransactionDefinition.PROPAGATION_MANDATORY | 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。 |
TransactionDefinition.PROPAGATION_NESTED | 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。 |
Spring事务简单应用
基于Spring AOP来实现Spring事务,加了注解@EnableTransactionManagement开启事务后,在类或者public方法上加上@Transactional注解就可以了。
pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
AppConfig.java
@ComponentScan("com.nxj.dev")
@Configuration
@EnableTransactionManagement
public class AppConfig {
@Bean
public JdbcTemplate jdbcTemplate() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
return jdbcTemplate;
}
@Bean
public PlatformTransactionManager transactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource());
// 默认是true(事务 部分失败时,全局回滚)
// 如果是false(事务 部分失败时,不回滚,都提交)
// transactionManager.setGlobalRollbackOnParticipationFailure(true);
return transactionManager;
}
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://ip:3306/spring_transaction?characterEncoding=utf-8&useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
}
OrderService.java
@Component
public class OrderService {
@Autowired
JdbcTemplate jdbcTemplate;
@Autowired
OrderService orderService;
// 1和2和3都插入失败
@Transactional
public void test1() {
// 1
jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
// 2
jdbcTemplate.execute("insert into order_info values ('009', 9.99)");
// 报错:java.lang.ArithmeticException: / by zero
// Spring事务默认会在遇到RuntimeException、Error以及它们的子类的异常时,会回滚事务
// 而ArithmeticException是RuntimeException的子类
int i = 1/0;
// 3
jdbcTemplate.execute("insert into order_info values ('010', 10.00)");
}
// 1和2和3都成功
@Transactional
public void test2() throws SQLException {
// 1
jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
// 2
jdbcTemplate.execute("insert into order_info values ('009', 9.99)");
// 3
jdbcTemplate.execute("insert into order_info values ('010', 10.00)");
// Spring事务默认会在遇到RuntimeException、Error以及它们的子类的异常时,会回滚事务
throw new SQLException();
}
// 1和2和3都失败
@Transactional(rollbackFor = Exception.class)
public void test3() throws SQLException {
// 1
jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
// 2
jdbcTemplate.execute("insert into order_info values ('009', 9.99)");
// 3
jdbcTemplate.execute("insert into order_info values ('010', 10.00)");
// Spring事务默认会在遇到RuntimeException、Error以及它们的子类的异常时,会回滚事务
throw new SQLException();
}
// 1和2和3都失败
@Transactional
public void test4() throws RuntimeException {
// 1
jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
// 2
jdbcTemplate.execute("insert into order_info values ('009', 9.99)");
// 3
jdbcTemplate.execute("insert into order_info values ('010', 10.00)");
// Spring事务默认会在遇到RuntimeException、Error以及它们的子类的异常时,会回滚事务
// throw new RuntimeException();
throw new Error();
}
// 1和2都插入成功,3 失败
public void test5() {
a();
}
// 1和2和3 都失败
public void test6() {
// 在同一个类中,非事务方法调用事务方法,事务失效,得采用(Service)AopContext.currentProxy().xx()来进行调用,事务才能生效。
((OrderService) AopContext.currentProxy()).a();
}
// 1和2和3 都失败
public void test7() {
// 在同一个类中,非事务方法调用事务方法,事务失效,也可以采用自己注入自己,事务能生效。
orderService.a();
}
@Transactional
public void a() {
// 1
jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
// 2
jdbcTemplate.execute("insert into order_info values ('009', 9.99)");
// 报错:java.lang.ArithmeticException: / by zero
int i = 1/0;
// 3
jdbcTemplate.execute("insert into order_info values ('010', 10.00)");
}
// 1和2和3都失败
@Transactional
public void test8() {
// 1
jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
b();
}
// 加不加@Transactional注解,都无所谓,因为同一个类中方法之间直接调用,被调用的方法上@Transactional失效
@Transactional
public void b() {
// 2
jdbcTemplate.execute("insert into order_info values ('009', 9.99)");
int i = 1/0;
// 3
jdbcTemplate.execute("insert into order_info values ('010', 10.00)");
}
// 1和2成功,3失败
@Transactional
public void test9() {
// 1
jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
try {
b();
} catch (Exception e) {
}
}
// 1和2和3失败
@Transactional
public void test10() {
// 1
jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
try {
b();
} catch (Exception e) {
//手动强制回滚事务,然后可以抛出自定义的异常给前端
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
// 1和2和3失败
@Transactional
public void test11() {
// 1
jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
try {
orderService.b();
} catch (Exception e) {
}
}
// 1和2都成功,3失败
@Transactional
public void test12() {
// 1
jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
orderService.c();
}
@Transactional
public void c() {
try {
// 2
jdbcTemplate.execute("insert into order_info values ('009', 9.99)");
int i = 1/0;
// 3
jdbcTemplate.execute("insert into order_info values ('010', 10.00)");
} catch (Exception e) {
}
}
}
@EnableTransactionManagement
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
/*
* proxyTargetClass
* true:
* 无论目标方法是否实现了接口,都使用CGLIB代理
* false:默认值
* 如果目标方法实现了接口,使用JDK动态代理
* 如果目标方法没有实现接口,使用CGLIB代理
*/
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
可以看到,这个注解也挺简单的,就是通过@Import注解,引入了另外一个bean,通过查看TransactionManagementConfigurationSelector的类继承关系,可以发现,这个类实现了ImportSelector注解,所以,会实现selectImports()方法,在该方法中,注入了两个重要的bean
AutoProxyRegistrar
ProxyTransactionManagementConfiguration
AutoProxyRegistrar
AutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,所以,在registerBeanDefinitions方法中,注入一个bean
InfrastructureAdvisorAutoProxyCreator 可以发现,这个类是一个后置处理器,继承了InstantiationAwareBeanPostProcessor
所以:这就是@EnableTransactionManagement的第一个作用,注入了一个后置处理器,这个后置处理器就是用来对事务注解进行增强的
ProxyTransactionManagementConfiguration
该类只有一个注解@Configuration,所以该类是一个配置类,在该类中,通过三个@Bean注解,向spring容器中注入了事务执行时要用到的组件
/**
* @return
* 注入事务增强器
* 这里是创建一个advisor,然后设置切点(TransactionInterceptor)和通知(TransactionAttributeSource)
* 这里的BeanFactoryTransactionAttributeSourceAdvisor类似于aop中的advisor
*/
@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;
}
/**
* @return
* 往spring容器中注入事务解析器(解析事务注解上的各个参数)
* 在执行第八个后置处理器的时候,判断是否需要增强的时候,会解析transaction注解
*
* 这里在new AnnotationTransactionAttributeSource()对象的时候,有一个非常关键的点:
* publicMethodsOnly 这里在调用构造函数的时候,默认初始化该值为true;该值的意思是:只允许public方法进行事务代理
*
* 在后面判断是否可以对方法进行增强的时候,会判断该值,以及对应method是否是public,如果是非public修饰的方法,直接return null,不进行代理增强
*/
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
/**
* @return
* 定义事务拦截器,并将事务拦截器注入到spring容器中
*/
@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容器中,注入了三个bean,分别是:事务拦截器、事务解析器、事务增强器
Advisor=Advice+Pointcut
其中
Advisor=>BeanFactoryTransactionAttributeSourceAdvisor
Advice=>TransactionInterceptor
Pointcut=>TransactionAttributeSource=>TransactionAttributeSourcePointcut