需要了解@Import註解的應用https://blog.csdn.net/u014203449/article/details/86559350
後置處理器https://blog.csdn.net/u014203449/article/details/86665963
代理模式和方法攔截器https://blog.csdn.net/u014203449/article/details/105707730
一.演示案例
環境搭建:
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執行新增過後, 數據庫數據沒有變化,因爲異常導致事務回滾。
二.@EnableTransactionManagement
和AOP 的@Enable註解一樣,看看它給容器注入了什麼?
TransactionManagementConfigurationSelector,實現了ImportSelector接口,就看 selectImports方法的返回結果是什麼,就會往容器中注入什麼組件。
根據adviceMode的值去注入不同的組件。
adviceMode的值在 EnableTransactionManagement 中 默認爲 AdviceMode.PROXY,所以會注入 AutoProxyRegistrar 和ProxyTransactionManagementConfiguration。
proxyTargetClass ,默認值爲false,一會用到。
1.AutoProxyRegistrar
實現了 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一樣,利用代理、增強器對原方法加強。
2.ProxyTransactionManagementConfiguration
給容器注入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))