今天我们来聊一聊spring的声明式事务,了解spring中事务处理即实现,接下来从代码准备、事务开启条件以及原理源码分析三个方面来展开说明。
代码准备
这部分我主要以基础的mysql加jdbcTemplate数据操作实现。
1、maven项目搭建,导入我们需要的数据库依赖以及spring操作数据库的依赖。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.29</version>
</dependency>
2、添加配置类,配置数据源、Spring提供操作数据库的工具JdbcTemplate以及事务管理器。
@EnableTransactionManagement
@Configuration
@ComponentScan(value = {"com.wjs.service","com.wjs.dao","com.wjs.tx"})
public class TxMainConfig {@Bean
public DataSource dataSource() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("123456");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate() throws Exception{
return new JdbcTemplate(dataSource());
}
@Bean
public PlatformTransactionManager platformTransactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource());
}
}
3、对应的事务方法Service类,以及数据库操作Dao类,最后我们的测试类。
@Service
public class BookService {
@Autowired
private BookDao bookDao;
@Transactional
public void insert() {
String name = "beiecanshijie";
String author = "lisi";
int price = 30;
bookDao.insert(name, author, price);
System.out.println("插入完成!");
int j = 1/0;
}}
@Repository
public class BookDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void insert(String name,String author,int price){
String sql = "INSERT into `book` (name,author,price) VALUES (?,?,?)";
jdbcTemplate.update(sql, name,author,price);
}
}
public class SpringTxTest {
@Test
public void test(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TxMainConfig.class);
BookService bookService = applicationContext.getBean(BookService.class);
bookService.insert();
}}
4、最后因为我们加入了事务,所以在Service层抛出除0异常后,数据库并不会插入一条数据,若注释掉 @Transactional注解则会插入一条数据,接下来说一说开启事务的条件。
事务开启条件
从上面的代码中,我们发现了两个新的注解 @Transactional 和 @EnableTransactionManagement,还有在配置类中我们加入了PlatformTransactionManager组件到容器中,这些就是开启事务的前提条件。
1、@Transactional,在我们的业务方法上加上该注解,表示当前方法是一个事务方法;
2、@EnableTransactionManagement,在我们的配置类上加入该注解表示开启基于注解的spring事务管理功能;
3,、添加PlatformTransactionManager组件,将该组件加入到spring容器中,配置事务管理器来控制事务;
原理分析
首先说一下@EnableTransactionManagement注解,他向容器中加入了TransactionManagementConfigurationSelector组件,因为EnableTransactionManagement里面AdviceMode默认的是proxy,所以它的目的就是向我们的容器中加入AutoProxyRegistrar,ProxyTransactionManagementConfiguration这两个组件。
AutoProxyRegistrar组件
进入代码,在重写的registerBeanDefinitions方法中,它会去执行registerAutoProxyCreatorIfNecessary这个方法,而这个方法就是向我们容器中注册一个InfrastructureAdvisorAutoProxyCreator组件,注册该组件的意义就是,利用后置处理器的机制,在我们bean对象创建后,对该bean进行一个包装,返回一个该bean的代理对象,这个代理对象中包括我们的一些增强器advisor,在代理对象执行方法的时候,会对这个增强器做一个转换,变为methodInerceptor,然后在目标方法执行时做一个拦截处理,这块跟之前我们了解的AnnotationAwareAspectJAutoProxyCreator组件的创建其实是一个道理,具体过程详情可参考之前的aop原理分析进行了解 https://blog.csdn.net/wjs2580219863/article/details/107191308。
ProxyTransactionManagementConfiguration组件
这个组件实际上是一个配置类,它向我们容器中注册了一些组件包括事务增强器、事务属性信息以及事务拦截器。
1、事务增强器,增强器加入了注解的属性信息AnnotationTransactionAttributeSource()以及事务拦截器。
注解属性详细信息 AnnotationTransactionAttributeSource中主要就是拿到 @Transactional 这个注解后面括号中加入的一些属性信息并解析。
2,事务拦截器,主要是返回一个TransactionInterceptor,这个对象加入了我们的事务属性信息以及事务管理器,点击这个对象我们了解到TransactionInterceptor是implements MethodInterceptor的,所以它是一个方法拦截器,也就是我们代理对象执行目标方法的时候会先去执行这个拦截器。
进入TransactionInterceptor对象的invoke方法,执行invokeWithinTransaction(),
1、首选会拿到我们的事务的一些属性信息,
2、再获取我们的事务管理器信息,
进入获取事务管理器方法,首先它会去判断这个qualifier有没有值,也就是去判断我们的事务方法@Transactional注解有没有添加transactionManager属性,没有设置的话,就去拿默认的事务管理器,当然默认的为空,然后spring就会根据这个PlatformTransactionManager类型从bean容器中去获取这个事务管理器,这就是我们为什么一开始在配置类中注册PlatformTransactionManager组件的原因,也是为什么一配置就能用事务管理器的原因。
3,继续往下走,invocation.proceedWithInvocation(),也就是我们之前了解过的chain拦截器链以及proceed方法执行部分,
如果事务方法抛出异常,则会在这个地方被捕获,然后执行completeTransactionAfterThrowing(txInfo, ex)方法,它实际上就是执行了事务管理器的回滚操作,txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());拿到事务管理器,执行回滚方法;
如果事务方法正常执行,则执行commitTransactionAfterReturning(txInfo)方法,进行事务的commit操作,拿到事务管理器执行commit;
综合以上信息,我们就对spring事务的配置以及实现的原理有了个大概的了解。