环境搭建: 1、导入相关依赖 数据源、数据库驱动、Spring-jdbc模块 2、配置数据源、JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据 3、给方法上标注 @Transactional 表示当前方法是一个事务方法; 4、 @EnableTransactionManagement 开启基于注解的事务管理功能; @EnableXXX 5、配置事务管理器来控制事务; @Bean public PlatformTransactionManager transactionManager()
@EnableTransactionManagement
@ComponentScan("com.atguigu.tx")
@Configuration
public class TxConfig {
//数据源
@Bean
public DataSource dataSource() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("wisdomclass");
dataSource.setPassword("susOn@20190916");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://xxxx:3306/intelligent_admin");
return dataSource;
}
//
@Bean
public JdbcTemplate jdbcTemplate() throws Exception{
//Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器中找组件
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
return jdbcTemplate;
}
//注册事务管理器在容器中
@Bean
public PlatformTransactionManager transactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource());
}
}
service,发生一次异常
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Transactional
public void insertUser(){
userDao.insert();
//otherDao.other();xxx
System.out.println("插入完成...");
int i = 10/0;
}
}
dao ,保存一条数据
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void insert(){
String sql = "INSERT INTO `user_info`(`name`,sex) VALUES(?,?)";
String username = UUID.randomUUID().toString().substring(0, 5);
jdbcTemplate.update(sql, username,1);
}
}
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(TxConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
userService.insertUser();
applicationContext.close();
}
结果sql执行新增过后, 数据库数据没有变化,因为异常导致事务回滚。
和AOP 的@Enable注解一样,看看它给容器注入了什么?
TransactionManagementConfigurationSelector,实现了ImportSelector接口,就看 selectImports方法的返回结果是什么,就会往容器中注入什么组件。
根据adviceMode的值去注入不同的组件。
adviceMode的值在 EnableTransactionManagement 中 默认为 AdviceMode.PROXY,所以会注入 AutoProxyRegistrar 和ProxyTransactionManagementConfiguration。
proxyTargetClass ,默认值为false,一会用到。
实现了 ImportBeanDefinitionRegistrar接口,可以用 BeanDefinitionRegistry 自定义往容器中注册组件。
可以打断点看看:
1.importingClassMetadata.getAnnotationTypes(); 得到一些标注的注解,并遍历
2.AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);得到注解的属性
3.如果注解的mode属性值不为空,且属性proxyTargetClass不为空,且...符合后面的条件,最终会执行AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
而proxyTargetClass ,默认值为false,forceAutoProxy不会执行。
会往容器中注入InfrastructureAdvisorAutoProxyCreator 实例
InfrastructureAdvisorAutoProxyCreator看继承关系,和AOP的AnnotationAwareAspectJAutoProxyCreator很像,是个BeanPostProcessor。
没有找到InfrastructureAdvisorAutoProxyCreator重写postProcessAfterInitialization等后置处理的方法,说明它的功能和AbstractAutoProxyCreator一样,如下和AOP一样。
打断点,查看userService的初始化(事务注解写在了userService上),
会发现它有个增强器,最终会返回userService的代理对象。
跟AOP一样,利用代理、增强器对原方法加强。
给容器注入BeanFactoryTransactionAttributeSourceAdvisor,TransactionAttributeSource(解析事务注解的属性),
TransactionInterceptor(方法拦截器)
TransactionInterceptor实现了MethodInterceptor,是个方法拦截器,在给userservice创建代理后,调用userService的方法时,会先执行它的invoke方法。
在单元测试调用userService保存时,打断点,看看流程。
进入断点,和AOP一样,进入了CglibAopProxy的intercept方法。
得到方法连接器链chain中有一个元素,就是TransactionInterceptor
最后构造 CglibMethodInvocation 执行proceed方法。
执行proceed方法,断点进入ReflectiveMethodInvocation的proceed
再进入断点,就来到了TransactionInterceptor 的invoke
invokeWithinTransaction方法:根据方法名来看看
1)、先获取事务相关的属性 getTransactionAttribute
2)、再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger,最终会从容器中按照类型获取一个PlatformTransactionManager;
3)、事务执行
创建事务(createTransactionIfNecessary)
--->执行目标方法(invocation.proceedWithInvocation())
----->如果异常,获取到事务管理器,利用事务管理回滚操作(completeTransactionAfterThrowing(txInfo, ex)); ----->如果正常,利用事务管理器,提交事务(commitTransactionAfterReturning(txInfo))
我进入 createTransactionIfNecessary 创建事务的方法找了一圈,发现没有用jdbc发送"begin"语句,而是使用Connection.setAutoCommit(false),把连接对象设置为非自动提交,如果是新事务就这样设置为非自动提交,其他情况有其他处理,是配置的DataSourceTransactionManager操作的。
当需要回滚最后使用的方法是 Connection.rollback,提交事务是Connection.commit。
总结:
@EnableTransactionManagement
在容器中引入了 一个BeanPostProcessor,跟aop一样对注解@Transication的类进行增强
引入一个增强器,TransactionInterceptor,注解了@Transication的方法会用TransactionInterceptor增强
TransactionInterceptor
- invokeWithinTransaction方法,明显的try catch代码块,使用我们配置的事务管理器(不配置有平台默认的) 进行开启事务、执行目标方法、异常回滚事务、正常提交事务
- 如果是新事务,使用Connection.setAutoCommit(false)就表示开启了
- 事务管理器最终调用的是 Connection.rollback回滚,Connection.commit。提交