在学习源码之前,我们先举一个例子来说明,我们准备使用jdbcTemplate通过c3p0连接数据库,然后进行测试我们的数据库在开启事务的情况下,在正常情况和抛异常的情况下有没有成功插入数据。
首先我们需要先引入我们待会需要的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
然后我们在数据库(test库)中建立一个测试的表(user表)
然后我们肯定需要连接上我们的数据库呀,我们就在配置类中简单的使用c3p0连接数据库了,并且在配置内中添加一个JdbcTemplate
接下来,我们就用我们就熟悉的方法来测试,在Dao层建立一个UserDao类,并使用@Autowired注入我们在配置内中添加的jdbcTemplate,用于向上表插入一条数据
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void insert(){
String sql = "insert into user(name,sex,age) values (?,?,?)";
jdbcTemplate.update(sql,"张三","男",22);
}
}
还有在Service层建立一个UserService调用UserDao的insert方法
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void addUser(){
userDao.insert();
}
}
好了,我们在测试内进行测试
public class TestMain {
@Test
public void test() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
userService.addUser();
}
}
好了,运行成功,我们可以看到在数据库中已经有了一条数据啦
然后我们就在UserService类的addUser方法上加上@Transactional注解,并人为制造一个异常出来
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Transactional
public void addUser(){
userDao.insert();
int i = 4 / 0;
}
}
然后我们再来运行,发现虽然报错了,但是数据库又出来一条数据
为什么呢?哦,我们想起来了,就像AOP一起,我们需要在配置类中,开启我们的事物管理
除了注解,在xml的开启也是可以的,xml方法如下
<!--使用annotation注解方式配置事务-->
<tx:annotation-driven transaction-manager="transactionManager"/>
//transactionManager是我们的事务管理器的name,下面会讲解
开启后,我们再次运行,报错了,如下
没事,我们再在配置类中,new一个出来
或者使用我们xml进行配置,也是有相应的配置的,如下
<!--事务管理-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
//开启事务时,配置的事务管理器就是这个
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
再次运行测试类,报错了,再次去查看数据库,也没有新的数据插入
所以发现我们的UserService类中addUser方法上加的事物注解生效了,我们可以将addUser上的@Transactional注解去掉,再运行就会发现,运行报错,但是数据库中就会多一条新的数据。
我们回顾下刚才的过程,和我们的AOP需要在配置类中开启AOP一样(@EnableAspectJAutoProxy),我们的事务也是需要在配置类上开始事务管理的(@EnableTransactionManagement),那么我们分析事务是不是可以也像分析AOP一样的步骤呢,我们一起看一看
进入我们开启事务管理的注解,发现是不是惊人的相似,它通过@Import注解引入一个类,我们再跟进
跟进后,我们发现这个类继承的父类很熟悉,另外我们下面还发现有三个参数,并且都含有默认值,其中第二个参数AdviceMode帮我们默认的是AdviceMode.PROXY,我们继续进入
哦,原来实现了ImportSelect的接口,我们之前Spring基础组件的使用——@Import详解中学习过呀
返回继续看我们的的TransactionManagementConfigurationSelector类,上面我们说了AdviceMode是有默认值的,根据其选项,进入其对应执行行,我们会发现两个比较重要的类,我们先进入第一个类
进入后发现,真好有时我们熟悉的东西,这个我们之前在Spring基础组件的使用——@Import详解中也学习过,并且我们的AOP也是使用这个方法注入了一个类的,我们向下看其方法
方法如下
之前我们已经找到默认AdviceMode.PROXY,我们继续跟进
发现和AOP一起,就是注册一个红框内的类,我们仔细观察一下,发现下面不就是我们AOP要注册的类的(红线处),原来它们两是调用的同一个方法注册的类呀,继续跟进
我们突然发现,不对呀,调用同样的方法,进入后注册的Bean的id是相同的,只有要注册的类不同,如果即开启了AOP,也开启了事务,那会注册哪一个类
不用担心,我们Spring会有一个优先级的,上图红线处,如果有优先级较高的,较低的就不用在注册一遍啦。那优先级是什么样的呢
因为是通过下标比较大小嘛,我们在比较一个我们AOP和事务管理通过这个相同的方法注册的Bean有什么区别
AOP————AspectJAwareAdvisorAutoProxyCreator.class
Transactional————InfrastructureAdvisorAutoProxyCreator.class
我们分别进入这两个类中,会发现这两个类继承了同一个父类
所以在到这里我们会发现我们的事务管理其实和AOP几乎大部分一模一样的,都是先注册一个类,然后也是通过对我们的业务类创建后初始化时,在后置处理器中对其进行增强处理的,也是通过生成代理类,有兴趣的话可以看一下我们的之前对AOP底层源码分析的博客。
我们再看一下我们注册的另一个类吧
进入后,我们看到其中在ProxyTransactionManagementConfiguration类中先new了一个类,然后进行其设值,我们分别进入其中查看,另外我们我们注意一下这个类的注解@Configuration,说明这是一个配置类,他里面的@Bean的方法都会被注入到Spring的容器中
继续跟进
跟进this,发现其调用了自身的本身的有参构造方法,然后继续跟进
进入后,发现这个方法内就是对我们@Transactional注解里面的属性进行解析之类的
我们回到ProxyTransactionManagementConfiguration类,查看下一个设置方法
然后发现这里也是new了一个类,我们进入查看
发现这个类继承了MethodInterceptor这个接口
看到这个接口,你有没有想到什么,对了,我们的AOP在对目标方法进行拦截的时候,也是把所有的增强处理方法转为MethodInterceptor这种形式,然后进行处理的嘛,如下绿色箭头处
AOP中就是把所有增强处理方法转为MethodInterceptor,添加到一个List集合中去,形成一个拦截链进行对目标方法的拦截,有兴趣的话可以查看Spring的AOP底层源码后续——AOP的调用流程及总结图解
在AOP拦截链中,我们每一个方法都有一个对应的类进行拦截,如@Before、@After等等,都有不同的类,我们事务管理肯定也是有一个类进行处理的,就是上述我们刚刚说的实现了MethodInterceptor接口的类
在AOP中,我们主要通过对应方法处理的各个类,以及ReflectMethodInvocation 类之间的invoke调用方法,进行链式拦截的,那我们事务管理中应该也是有个invoke方法的,果然在我们的TransactionInterceptor类中找到了invoke方法,我们进行查看
发现它调用了其父类的方法进行处理
然后我们一步步进行分析,大致查看
上述红框,就是我们进入查看过的,就是获取我们@Transactional注解里面的属性进行解析之类的,向下
这个就是我们的配置类中通过@Bean注入的platformTransactionManager,继续向下
点击进入
我们会发现其实就是相当于把我们的事务管理里开始管理我们的事务了,接下来成功了就提交,否则就回滚之类的
继续向下看,会发现就是会调用我们的目标方法,如果产生异常了就会被捕获到,从而进行回滚
如果执行成功了就进行提交