• Spring注解驱动开发5:Spring声明式事务


    Spring注解驱动开发系列:

    1. Spring注解驱动开发1:组件注册
    2. Spring注解驱动开发2:生命周期和属性赋值
    3. Spring注解驱动开发3:自动装配
    4. Spring注解驱动开发4:AOP使用和原理
    5. Spring注解驱动开发5:Spring声明式事务
    6. Spring注解驱动开发6:Spring扩展原理

    Spring注解驱动开发5:Spring声明式事务

    Spring声明式事务的使用

    环境搭建

    导入相关依赖

    • 数据源
    • 数据库驱动
    • Spring-jdbc模块

    pom文件增加如下:

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
    <dependency>
        <groupId>com.mchange</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.5.2</version>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    

    编写User类:

    @Component
    public class User {
        private String name;
        private int age;
    
        public User() {
    
        }
    
        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    

    编写UserDao类:

    @Repository
    public class UserDao {
    
        @Autowired
        JdbcTemplate jdbcTemplate;
    
        public void insert(User user) {
            String sql = "INSERT INTO tb_user(username, age) VALUES(?, ?)";
            jdbcTemplate.update(sql, user.getName(), user.getAge());
        }
    }
    

    编写UserService类:

    @Service
    public class UserService {
    
        @Autowired
        UserDao userDao;
    
        @Transactional
        public void insertUser(User user) {
            userDao.insert(user);
            System.out.println("insert fini");
            int i = 10/0;
        }
    }
    

    编写配置类:

    //配置类==配置文件
    @Configuration
    //开启自动包扫描,传入要扫描的包路径
    @ComponentScan(basePackages = "com.lin.springL")
    //开启注解事务支持
    @EnableTransactionManagement
    public class MainConfigure {
    
        @Bean
        public DataSource dataSource() throws PropertyVetoException {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setUser("root");
            dataSource.setPassword("root");
            dataSource.setDriverClass("com.mysql.jdbc.Driver");
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
            return dataSource;
        }
    
        @Bean
        public JdbcTemplate jdbcTemplate(DataSource dataSource) {
            return new JdbcTemplate(dataSource);
        }
    
        @Bean
        public PlatformTransactionManager transactionManager(DataSource dataSource) {
            return new DataSourceTransactionManager(dataSource);
        }
    }
    

    编写测试类:

    public class MainTest {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext applicationContext = new
                    AnnotationConfigApplicationContext(MainConfigure.class);
            UserService userService = applicationContext.getBean(UserService.class);
            User user = new User("ddd", 34);
            userService.insertUser(user);
        }
    }
    

    执行MainTest类,会报错,查看数据库数据并没有插入数据,事务回滚成功。

    总结

    总的步骤如下:

    1. 在配置类上增加@EnableTransactionManagement注解开启事务管理。
    2. 向容器中注入PlatformTransactionManager的实现Bean。
    3. 在需要食物的方法上增加注解:@Transactional

    源码简单分析

    还是以最重要的配置注解为开始查看:

    进入到EnableTransactionManagement类:

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import({TransactionManagementConfigurationSelector.class})
    public @interface EnableTransactionManagement {
        boolean proxyTargetClass() default false;
    
        AdviceMode mode() default AdviceMode.PROXY;
    
        int order() default 2147483647;
    }
    

    关注到@Import({TransactionManagementConfigurationSelector.class}) 向容器中注入了TransactionManagementConfigurationSelector这个类进入到这个类查看:

    public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
    
       /**
        * {@inheritDoc}
        * @return {@link ProxyTransactionManagementConfiguration} or
        * {@code AspectJTransactionManagementConfiguration} for {@code PROXY} and
        * {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()}, respectively
        */
       @Override
       protected String[] selectImports(AdviceMode adviceMode) {
          switch (adviceMode) {
             case PROXY:
                return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
             case ASPECTJ:
                return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
             default:
                return null;
          }
       }
    }
    

    image-20200519161014816

    这个类实现了ImportSelector接口,看具体实现方法selectImports,:

    • 如果是PROXY,就返回注册AutoProxyRegistrar和ProxyTransactionManagementConfiguration。
    • 如果是ASPECTJ,就返回注册TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME。

    因为其默认AdviceMode mode() default AdviceMode.PROXY,所以是走PROXY这个分支,返回注册AutoProxyRegistrar和ProxyTransactionManagementConfiguration。

    AutoProxyRegistrar

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        ...
             if (mode == AdviceMode.PROXY) {
                AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
        ...
    

    里面方法最主要是的就是上面这行,注册自动代理创建类,也就是AOP,所以可以大胆猜测,Spring的事务管理通过AOP代理实现,例如回滚操作,在AOP的AfterThrow中回滚就可以达到抛出异常的时候会滚事务的操作。

  • 相关阅读:
    细说 webpack 之流程篇
    git 撤销commit
    Git远程操作详解
    git Could not read from remote repository 解决
    Mysql 关键字及保留字
    使用 Intellij Idea 导出JavaDoc
    【树莓派】盒子常见问题处理基础帮助
    【树莓派】crontab设置Linux设备定时重启
    【医疗行业】关于dcm4che DICOM Toolkit:C-Move与C-Get
    关于操作系统:eos、deepin
  • 原文地址:https://www.cnblogs.com/jinchengll/p/12922355.html
Copyright © 2020-2023  润新知