我随意写,你随意看。

林老头儿

但愿绝望和无奈远走高飞

Fork me on Gitee

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中回滚就可以达到抛出异常的时候会滚事务的操作。

posted @ 2020-05-20 11:05  林老头儿  阅读(388)  评论(0编辑  收藏  举报