1、聲明式事務,境搭建環。在pom.xml配置文件中新增依賴的jar包,導入相關依賴,數據源、數據驅動、Spring-jdbc模塊。如下所示:
1 <project xmlns="http://maven.apache.org/POM/4.0.0" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 4 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 <groupId>com.bie</groupId> 7 <artifactId>spring-tx-sourceCode</artifactId> 8 <version>0.0.1-SNAPSHOT</version> 9 10 11 <dependencies> 12 <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> 13 <dependency> 14 <groupId>org.springframework</groupId> 15 <artifactId>spring-context</artifactId> 16 <version>4.3.12.RELEASE</version> 17 </dependency> 18 <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> 19 <dependency> 20 <groupId>org.projectlombok</groupId> 21 <artifactId>lombok</artifactId> 22 <version>1.18.8</version> 23 <scope>provided</scope> 24 </dependency> 25 <!-- https://mvnrepository.com/artifact/javax.inject/javax.inject --> 26 <dependency> 27 <groupId>javax.inject</groupId> 28 <artifactId>javax.inject</artifactId> 29 <version>1</version> 30 </dependency> 31 <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 --> 32 <dependency> 33 <groupId>com.mchange</groupId> 34 <artifactId>c3p0</artifactId> 35 <version>0.9.5.2</version> 36 </dependency> 37 <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> 38 <dependency> 39 <groupId>mysql</groupId> 40 <artifactId>mysql-connector-java</artifactId> 41 <version>5.1.44</version> 42 </dependency> 43 <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop --> 44 <dependency> 45 <groupId>org.springframework</groupId> 46 <artifactId>spring-aop</artifactId> 47 <version>4.3.12.RELEASE</version> 48 </dependency> 49 <dependency> 50 <groupId>org.springframework</groupId> 51 <artifactId>spring-aspects</artifactId> 52 <version>4.3.12.RELEASE</version> 53 </dependency> 54 <dependency> 55 <groupId>org.springframework</groupId> 56 <artifactId>spring-jdbc</artifactId> 57 <version>4.3.12.RELEASE</version> 58 </dependency> 59 60 </dependencies> 61 </project>
配置數據源,JdbcTemplate(Spring提供的簡化數據庫操作的工具)操作數據。使用@EnableTransactionManagement注解,開啟基於注解的事務管理功能。配置事務管理器來控制事務,注冊事務管理器PlatformTransactionManager到容器中。
1 package com.bie.tx; 2 3 import java.beans.PropertyVetoException; 4 5 import javax.sql.DataSource; 6 7 import org.springframework.context.annotation.Bean; 8 import org.springframework.context.annotation.ComponentScan; 9 import org.springframework.context.annotation.Configuration; 10 import org.springframework.jdbc.core.JdbcTemplate; 11 import org.springframework.jdbc.datasource.DataSourceTransactionManager; 12 import org.springframework.transaction.PlatformTransactionManager; 13 import org.springframework.transaction.annotation.EnableTransactionManagement; 14 15 import com.mchange.v2.c3p0.ComboPooledDataSource; 16 17 /** 18 * 19 * 20 * @Title: TransactionConfig.java 21 * @Package com.bie.tx 22 * @Description: TODO 23 * @author biehl 24 * @date 2019年12月19日 25 * @version V1.0 26 * 27 * 聲明式事務,境搭建環 28 * 29 * 1、導入相關依賴,數據源、數據驅動、Spring-jdbc模塊。 30 * 2、配置數據源,JdbcTemplate(Spring提供的簡化數據庫操作的工具)操作數據。 31 * 3、@EnableTransactionManagement注解,開啟基於注解的事務管理功能。 32 * 33 * 4、配置事務管理器來控制事務。 34 * 35 */ 36 @EnableTransactionManagement // 第一步、開啟基於注解的事務管理功能。 37 @Configuration 38 @ComponentScan(basePackages = { "com.bie" }) 39 public class TransactionConfig { 40 41 /** 42 * 配置連接mysql的配置信息 43 * 44 * @return 45 * @throws PropertyVetoException 46 */ 47 @Bean 48 public DataSource dataSource() throws PropertyVetoException { 49 ComboPooledDataSource dataSource = new ComboPooledDataSource(); 50 dataSource.setUser("root"); 51 dataSource.setPassword("123456"); 52 dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/biehl"); 53 dataSource.setDriverClass("com.mysql.jdbc.Driver"); 54 return dataSource; 55 } 56 57 /** 58 * 從數據源中獲取到連接 59 * 60 * @param dataSource 61 * 從IOC容器中獲取到DataSource對象 62 * @return 63 */ 64 @Bean 65 public JdbcTemplate jdbcTemplate(DataSource dataSource) { 66 // Spring對@Configuration會進行特殊處理。給容器加入組件的方法,多次調用都只是從容器中找組件而已。 67 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); 68 // JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource()); 69 return jdbcTemplate; 70 } 71 72 /** 73 * 第三步、注冊事務管理器到容器中 74 * 75 * @param dataSource 76 * 事務管理器要管理好數據源 77 * @return 78 */ 79 @Bean 80 public PlatformTransactionManager platformTransactionManager(DataSource dataSource) { 81 return new DataSourceTransactionManager(dataSource); 82 } 83 84 }
事務配置三步走。
第一步、開啟基於注解的事務管理功能。
第二步、事務方法,如果程序出現異常進行回滾操作。
第三步、注冊事務管理器到容器中。
創建User實體類,如下所示:
1 package com.bie.po; 2 3 import lombok.AllArgsConstructor; 4 import lombok.Data; 5 import lombok.NoArgsConstructor; 6 7 /** 8 * 9 * 10 * @Title: User.java 11 * @Package com.bie.po 12 * @Description: TODO 13 * @author biehl 14 * @date 2019年12月19日 15 * @version V1.0 16 * 17 */ 18 @Data 19 @AllArgsConstructor 20 @NoArgsConstructor 21 public class User { 22 23 private int id; 24 private String name; 25 private int age; 26 }
創建dao層的數據交互層,用於和數據庫進行交互的代碼。
1 package com.bie.dao; 2 3 /** 4 * 5 * 6 * @Title: UserDao.java 7 * @Package com.bie.dao 8 * @Description: TODO 9 * @author biehl 10 * @date 2019年12月19日 11 * @version V1.0 12 * 13 */ 14 public interface UserDao { 15 16 /** 17 * 18 */ 19 public void insert(); 20 21 }
1 package com.bie.dao.impl; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.jdbc.core.JdbcTemplate; 5 import org.springframework.stereotype.Repository; 6 7 import com.bie.dao.UserDao; 8 9 /** 10 * 11 * 12 * @Title: UserDaoImpl.java 13 * @Package com.bie.dao 14 * @Description: TODO 15 * @author biehl 16 * @date 2019年12月19日 17 * @version V1.0 18 * 19 */ 20 @Repository 21 public class UserDaoImpl implements UserDao { 22 23 @Autowired 24 private JdbcTemplate jdbcTemplate; 25 26 public void insert() { 27 String sql = "INSERT INTO biehl.user( name, age) VALUES (?, ?)"; 28 String username = "張三三"; 29 int age = 25; 30 jdbcTemplate.update(sql, username, age); 31 } 32 33 }
創建service層的業務邏輯層,用於調用dao層。
1 package com.bie.service; 2 3 /** 4 * 5 * 6 * @Title: UserService.java 7 * @Package com.bie.service 8 * @Description: TODO 9 * @author biehl 10 * @date 2019年12月19日 11 * @version V1.0 12 * 13 */ 14 public interface UserService { 15 16 /** 17 * 18 */ 19 public void insert(); 20 }
1 package com.bie.service.impl; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.stereotype.Service; 5 import org.springframework.transaction.annotation.Transactional; 6 7 import com.bie.dao.UserDao; 8 import com.bie.service.UserService; 9 10 /** 11 * 12 * 13 * @Title: UserServiceImpl.java 14 * @Package com.bie.service.impl 15 * @Description: TODO 16 * @author biehl 17 * @date 2019年12月19日 18 * @version V1.0 19 * 20 * Service層添加事務,如果在程序出現異常,之前執行的sql出現回滾。 21 * 22 * 給方法標注@Transactional,表示當前方法是一個事務方法。 23 */ 24 @Service 25 public class UserServiceImpl implements UserService { 26 27 @Autowired 28 private UserDao userDao; 29 30 /** 31 * 32 */ 33 @Override 34 @Transactional // 第二步、事務方法,如果程序出現異常進行回滾操作。 35 public void insert() { 36 userDao.insert(); 37 System.out.println("插入成功了咯......."); 38 int i = 1 / 0; 39 } 40 41 }
主啟動類,如下所示:
1 package com.bie.main; 2 3 import org.springframework.context.annotation.AnnotationConfigApplicationContext; 4 5 import com.bie.service.UserService; 6 import com.bie.tx.TransactionConfig; 7 8 /** 9 * 10 * 11 * @Title: SpringTransactionApplication.java 12 * @Package com.bie.main 13 * @Description: TODO 14 * @author biehl 15 * @date 2019年12月19日 16 * @version V1.0 17 * 18 */ 19 public class SpringTransactionApplication { 20 21 public static void main(String[] args) { 22 AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TransactionConfig.class); 23 UserService userService = ac.getBean(UserService.class); 24 userService.insert(); 25 26 ac.close(); 27 } 28 29 }
2、Spring事務源碼分析,如下所示:
默認之前分析AOP源碼,首先從這個注解@EnableTransactionManagement,開啟基於注解的事務管理功能,開始進行研究。
該注解@EnableTransactionManagement利用TransactionManagementConfigurationSelector給容器中會導入組件。
即該注解@Import(TransactionManagementConfigurationSelector.class)給容器中會導入組件,TransactionManagementConfigurationSelector是一個Selector。
@EnableTransactionManagement利用TransactionManagementConfigurationSelector給容器中會導入組件。
點進去TransactionManagementConfigurationSelector可以看到,TransactionManagementConfigurationSelector繼承了AdviceModeImportSelector<EnableTransactionManagement>。
點進去AdviceModeImportSelector可以看到,抽象類AdviceModeImportSelector<A extends Annotation>實現了接口ImportSelector。
ImportSelector是Import的選擇器。ImportSelector是一個接口,規定方法selectImports,返回值String[]字符串數組,數組里面就是類的全類名,返回需要導入的組件的全類名數組。
由於接口ImportSelector只有一個方法String[] selectImports(AnnotationMetadata importingClassMetadata);而類TransactionManagementConfigurationSelector進行了實現,所以將斷點打到該方法上。
由於方法棧,里面步驟很多,之前分析AOP源碼的時候,大概過了一下,這里自己也可以進行分析即可。分析以后走到斷點處。
經過上述分析,可以看到該注解@EnableTransactionManagement給Spring容器導入了兩個組件AutoProxyRegistrar, ProxyTransactionManagementConfiguration。
3、這兩個組件AutoProxyRegistrar、ProxyTransactionManagementConfiguration實現了什么功能嗎?
AutoProxyRegistrar實現了ImportBeanDefinitionRegistrar接口,該接口ImportBeanDefinitionRegistrar是一個接口,規定方法registerBeanDefinitions,返回值是空void。通過調用該方法自己給容器中添加組件。importingClassMetadata是當前類的注解信息,registry是bean定義的注冊類,使用registry給容器注冊一個bean實例。
AutoProxyRegistrar類給容器中注冊了一個組件,InfrastructureAdvisorAutoProxyCreator(即基本的增強器自動代理創建組件)。分析InfrastructureAdvisorAutoProxyCreator既可以分析出AutoProxyRegistrar的功能了。
點進去看看,AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
InfrastructureAdvisorAutoProxyCreator的功能是什么呢,利用后置處理器機制對象創建以后,包裝對象,返回一個代理對象(增強器)。代理對象執行方法利用攔截器鏈進行調用。
ProxyTransactionManagementConfiguration類是一個配置類,給容器中事務增強器BeanFactoryTransactionAttributeSourceAdvisor(transactionAdvisor),事務增強器要用事務注解的信息。
給容器中注冊事務增強器,advisor.setTransactionAttributeSource(transactionAttributeSource());事務增強器要用事務注解的信息,AnnotationTransactionAttributeSource解析事務注解。
給容器中注冊事務增強器,advisor.setAdvice(transactionInterceptor());事務的攔截器,保存了事務的屬性信息,包含了事務管理器。TransactionInterceptor的底層是MethodInterceptor即方法攔截器。什么是方法攔截器呢,比如給容器放入一個代理對象,代理對象要執行目標方法的時候,方法攔截器就會工作。
給容器中注冊事務增強器,advisor.setTransactionAttributeSource(transactionAttributeSource());事務增強器要用事務注解的信息,AnnotationTransactionAttributeSource解析事務注解。
return new AnnotationTransactionAttributeSource();點擊進去,可以看到this.annotationParsers.add(new SpringTransactionAnnotationParser());該SpringTransactionAnnotationParser類解析事務注解的。
給容器中注冊事務增強器,advisor.setAdvice(transactionInterceptor());事務的攔截器,保存了事務的屬性信息,包含了事務管理器。TransactionInterceptor的底層是MethodInterceptor即方法攔截器。什么是方法攔截器呢,比如給容器放入一個代理對象,代理對象要執行目標方法的時候,方法攔截器就會工作。
TransactionInterceptor interceptor = new TransactionInterceptor();類點進去。
MethodInterceptor在目標方法執行的時候,執行攔截器鏈,此時只有一個攔截器,即事務攔截器TransactionInterceptor。
該事務攔截器的功能,先獲取事務相關的屬性,再獲取PlatformTransactionManager平台事務管理器,如果事先沒有添加指定任何transactionManager,最終會從spring容器中按照類型進行獲取一個PlatformTransactionManager。
執行目標方法,如果正常,利用事務管理器提交事務。
執行目標方法,如果異常,獲取到事務管理器,利用事務管理器進行回滾此次操作,真正的回滾與提交是事務管理器進行操作的。MethodInterceptor只是對方法進行攔截。
4、事務控制的過程步驟,事務的執行流程和AOP的源碼分析一致,首先使用注解@EnableTransactionManagement開啟事務管理,然后使用AutoProxyRegistrar注冊一個后置處理器InfrastructureAdvisorAutoProxyCreator,后置處理器負責包裝代理對象。再注冊ProxyTransactionManagementConfiguration一個配置類,配置類里面有AnnotationTransactionAttributeSource事務增強器(事務增強器要用事務注解的信息,解析事務的注解屬性信息)、TransactionInterceptor事務攔截器,代理對象執行目標方法就會執行到當前容器里面攔截器鏈,每次執行如果異常利用事務管理器進行回滾操作,如果正常利用事務管理器,提交事務。
作者:別先生
博客園:https://www.cnblogs.com/biehongli/
如果您想及時得到個人撰寫文章以及著作的消息推送,可以掃描上方二維碼,關注個人公眾號哦。