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事务拦截器,代理对象执行目标方法就会执行到当前容器里面拦截器链,每次执行如果异常利用事务管理器进行回滚操作,如果正常利用事务管理器,提交事务。