Spring之事務源碼理解,Spring4.3.12.RELEASE版本


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/

如果您想及時得到個人撰寫文章以及著作的消息推送,可以掃描上方二維碼,關注個人公眾號哦。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM