【spring注解驱动开发】- 申明式事务 - 测试和源码分析

本博客demo源码地址
https://github.com/suchahaerkang/spring-annotation.git

1 测试

首先向项目中引入数据源(c3p0),mysql连接驱动和 spring-jdbc的jar包

<!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->
<dependency>
	<groupId>c3p0</groupId>
	<artifactId>c3p0</artifactId>
	<version>0.9.1.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.44</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.2.RELEASE</version>
</dependency>

写个配置类

/**
 * @description:
 * @author: sukang
 * @date: 2020-03-13 8:58
 */
@EnableTransactionManagement
@ComponentScan("com.wolfx.tx")
@Configuration
public class MainConfigOfTx {

    /**
     * @description: 配置数据源
     * @param
     * @return: javax.sql.DataSource
     * @author: sukang
     * @date: 2020/3/13 9:06
     */
    @Bean
    public DataSource dataSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/dev");
        dataSource.setUser("root");
        dataSource.setPassword("root");
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }

    /**
     * @description: 将JdbcTemplate组件注册到容器中去
     * @param
     * @return: org.springframework.jdbc.core.JdbcTemplate
     * @author: sukang
     * @date: 2020/3/13 10:06
     */
    @Bean
    public JdbcTemplate jdbcTemplate() throws PropertyVetoException {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());

        return jdbcTemplate;
    }

    /**
     * @description: 将DataSourceTransactionManager的事务管理器注册到容器中去
     * @param
     * @return: org.springframework.transaction.PlatformTransactionManager
     * @author: sukang
     * @date: 2020/3/13 10:06
     */
    @Bean
    public PlatformTransactionManager transactionManager() throws PropertyVetoException {
        return new DataSourceTransactionManager(dataSource());
    }
}

写两个业务逻辑组件UserService和UserDao

/**
 * @description:
 * @author: sukang
 * @date: 2020-03-13 9:09
 */
@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    @Transactional
    public void insert(){
        userDao.insert();
    }
}


/**
 * @description:
 * @author: sukang
 * @date: 2020-03-13 9:09
 */
@Repository
public class UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void insert(){
        String sql = "INSERT INTO `tb_user`(name,age) VALUES(?, ?)";
        String str = UUID.randomUUID().toString().substring(0,5);
        jdbcTemplate.update(sql, str, 19);
        System.out.println("插入成功");
    }

}

数据库中创建一个tb_user表
在这里插入图片描述
写个测试用例

 @Test
    public void test01(){
        //创建IOC容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfTx.class);
        //获取UserService组件
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.insert();
    }

运行结果
在这里插入图片描述
数据库中有了一条数据
在这里插入图片描述
我们现在在UserService组件的insert()方法中修改一下代码,让其报异常,我们看看执行insert方法出现异常后,还能往数据库中插入数据吧?

/**
 * @description:
 * @author: sukang
 * @date: 2020-03-13 9:09
 */
@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    @Transactional
    public void insert(){
        userDao.insert();
        int i = 10/0;
    }
}

运行结果
在这里插入图片描述
插入成功之后,然后就包异常了,我们再看看数据中的数据
在这里插入图片描述
数据库中还是只有一条数据,说明插入成功的那条数据在抛出异常之后回滚了。
申明式事务测试成功!想实现申明式事务必须要以下几步:
1) 在配置类上边标注@EnableTransactionManagement开启申明式事务
2) 在配置文件中必须要注册PlatformTransactionManager的事务管理器
3) 在需要事务处理的方法上加上@Transactional注解

2 源码分析

Spring的申明式事务用的是Spring的AOP技术,想了解Spring AOP技术可以了解一下我的这篇博客,我们现在从@EnableTransactionManagement这个注解作为入口,分析一下这个注解做了什么?

2.1 @EnableTransactionManagement

在这里插入图片描述
不了解@Import注解的作用的可以看一下我这篇文章
在这里插入图片描述
因为adviceMode默认是PROXY,所以@EnableTransactionManagement利用TransactionManagementConfigurationSelector组件给我们向容器注入了AutoProxyRegistrar和ProxyTransactionManagementConfiguration组件
下面我们分析一下这两个组件做了什么?

2.2 AutoProxyRegistrar

AutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口重写registerBeanDefinitions()方法会手动像容器中注册组件
在这里插入图片描述
在这里插入图片描述
跟源码,发现给我们手动注册了InfrastructureAdvisorAutoProxyCreator这个组件,这个组件又是做什么的呢?我们继续看源代码
在这里插入图片描述
最后发现InfrastructureAdvisorAutoProxyCreator implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware。看到这两个组件,我们就明白了,因为这篇文章我们详细的分析了其作用,这里我们就说了。这说明InfrastructureAdvisorAutoProxyCreator这组件其实就是一个InstantiationAwareBeanPostProcessor后置处理器,会在所有bean初始化之后拦截,判断当前bean是否需要增强,如果需要的话,那么给其包装成一个代理对象。当使用这个bean的方法的时候,代理对象会拦截进行功能增强。好了,我们知道@EnableTransactionManagement注解给我们注册的AutoProxyRegistrar这个组件是给我们注册了InfrastructureAdvisorAutoProxyCreator这个组件,并且知道了其作用。下面我们看一下@EnableTransactionManagement给我们注册的另外一个组件ProxyTransactionManagementConfiguration的作用

2.3 ProxyTransactionManagementConfiguration

在这里插入图片描述
我们看到ProxyTransactionManagementConfiguration就是个配置类,给我们向容器注册了BeanFactoryTransactionAttributeSourceAdvisor这个事务增强器,BeanFactoryTransactionAttributeSourceAdvisor设置了TransactionAttributeSource和TransactionInterceptor这两个属性

2.3.1 TransactionAttributeSource的作用

在这里插入图片描述

2.3.2 TransactionInterceptor的作用

在这里插入图片描述
我们发现TransactionInterceptor实现了MethodInterceptor接口,那么在执行目标方法的时候,会被执行拦截器链,执行到这个事务拦截器会做一下操作
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

发布了88 篇原创文章 · 获赞 34 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/suchahaerkang/article/details/104833533
今日推荐