【小家Spring】从基于@Transactional全注解方式的声明式事务入手,彻底掌握Spring事务管理的原理(上)

简介: 【小家Spring】从基于@Transactional全注解方式的声明式事务入手,彻底掌握Spring事务管理的原理(上)

前言


上篇文章:

【小家Spring】Spring-jdbc的使用以及Spring事务管理的8种方式介绍(声明式事务+编程式事务)

介绍了Spring事务的众多使用方式,其中讲到全注解@Transactional方式的时候一笔带过了,那么本文就以当下最流行的Spring事务的使用方式:全注解的@Transactional使用方式为切入点,扒开Spring事务管理的神秘面纱~


全注解@Transactional方式的Spring事务


SpringBoot大行其道的今天,基于XML配置的Spring Framework的使用方式注定已成为过去式。

注解驱动应用,面向元数据编程已然成受到越来越多开发者的偏好了,毕竟它的便捷程度、优势都是XML方式不可比拟的。


对SpringBoot有多了解,其实就是看你对Spring Framework有多熟悉~ 比如SpringBoot大量的模块装配的设计模式,其实它属于Spring Framework提供的能力


@Transactional的使用

1、开启注解驱动


@EnableTransactionManagement // 开启注解驱动
@Configuration
public class JdbcConfig { ... }


提示:使用@EnableTransactionManagement注解前,请务必保证你已经配置了至少一个PlatformTransactionManager的Bean,否则会报错。(当然你也可以实现TransactionManagementConfigurer来提供一个专属的,只是我们一般都不这么去做~~~)


2、在你想要加入事务的方法上(或者类(接口)上)标注@Transactional注解

@Service
public class HelloServiceImpl implements HelloService {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Transactional
    @Override
    public Object hello() {
        // 向数据库插入一条记录
        String sql = "insert into user (name,age) values ('fsx',21)";
        jdbcTemplate.update(sql);
        // 做其余的事情  可能抛出异常
        System.out.println(1 / 0);
        return "service hello";
    }
}


单元测试:


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RootConfig.class, JdbcConfig.class})
public class TestSpringBean {
    @Autowired
    private HelloService helloService;
    @Test
    public void test1() {
        System.out.println(helloService.getClass()); //class com.sun.proxy.$Proxy29
        helloService.hello();
    }
}


就这么简单,事务就生效了(这条数据并没有insert成功~)。


从使用步骤上,有没有一种似曾相识的感觉??

没错,特别特别的像@Async或者@Scheduled的使用。其实,原理和@Async也非常的类似(其实还有有本质区别的,一个借助的自动代理创建器,一个自己使用的后置处理器),因此强烈建议可议先参阅博文:

【小家Spring】Spring异步处理@Async的使用以及原理、源码分析(@EnableAsync)

还有这个:

【小家Spring】Spring AOP的核心类:AbstractAdvisorAutoProxy自动代理创建器深度剖析(AnnotationAwareAspectJAutoProxyCreator)


接下来分析注解驱动事务的原理,同样的我们从@EnableTransactionManagement开始:

@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 Ordered.LOWEST_PRECEDENCE;
}


简直不要太面熟好不好,属性和@EnableAsync注解的一毛一样。不同之处只在于@Import导入器导入的这个类,但是我们依然能发现端倪:它也是个ImportSelector


@EnableAsync注解源码:


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
  // 支持自定义注解类型 去支持异步~~~
  Class<? extends Annotation> annotation() default Annotation.class;
  boolean proxyTargetClass() default false;
  AdviceMode mode() default AdviceMode.PROXY;
  int order() default Ordered.LOWEST_PRECEDENCE;
}


TransactionManagementConfigurationSelector

它所在的包为org.springframework.transaction.annotation,jar属于:spring-tx(若引入了spring-jdbc,这个jar会自动导入)


public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
  @Override
  protected String[] selectImports(AdviceMode adviceMode) {
    switch (adviceMode) {
      // 很显然,绝大部分情况下,我们都不会使用AspectJ的静态代理的~~~~~~~~
      // 这里面会导入两个类~~~
      case PROXY:
        return new String[] {AutoProxyRegistrar.class.getName(),
            ProxyTransactionManagementConfiguration.class.getName()};
      case ASPECTJ:
        return new String[] {determineTransactionAspectClass()};
      default:
        return null;
    }
  }
  private String determineTransactionAspectClass() {
    return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
        TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
        TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
  }
}


依然可以看出,和@EnableAsync导入的AsyncConfigurationSelector如出一辙,都继承自AdviceModeImportSelector,这就是为何我强烈建议先看关于@Async那篇博文的原因,毕竟模式一样,触类旁通,一通百通~


AdviceModeImportSelector目前所知的三个子类是:AsyncConfigurationSelector、TransactionManagementConfigurationSelector、CachingConfigurationSelector。由此可见后面还会着重分析的Spring的缓存体系@EnableCaching,模式也是和这个极其类似的~~~

AutoProxyRegistrar


从名字是意思是:自动代理注册器。它是个ImportBeanDefinitionRegistrar,可以实现自己向容器里注册Bean的定义信息


// @since 3.1
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    boolean candidateFound = false;
    // 这里面需要特别注意的是:这里是拿到所有的注解类型~~~而不是只拿@EnableAspectJAutoProxy这个类型的
    // 原因:因为mode、proxyTargetClass等属性会直接影响到代理得方式,而拥有这些属性的注解至少有:
    // @EnableTransactionManagement、@EnableAsync、@EnableCaching等~~~~
    // 甚至还有启用AOP的注解:@EnableAspectJAutoProxy它也能设置`proxyTargetClass`这个属性的值,因此也会产生关联影响~
    Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
    for (String annoType : annoTypes) {
      AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
      if (candidate == null) {
        continue;
      }
      // 拿到注解里的这两个属性
      // 说明:如果你是比如@Configuration或者别的注解的话  他们就是null了
      Object mode = candidate.get("mode");
      Object proxyTargetClass = candidate.get("proxyTargetClass");
      // 如果存在mode且存在proxyTargetClass 属性
      // 并且两个属性的class类型也是对的,才会进来此处(因此其余注解相当于都挡外面了~)
      if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
          Boolean.class == proxyTargetClass.getClass()) {
        // 标志:找到了候选的注解~~~~
        candidateFound = true;
        if (mode == AdviceMode.PROXY) {
          // 这一部是非常重要的~~~~又到了我们熟悉的AopConfigUtils工具类,且是熟悉的registerAutoProxyCreatorIfNecessary方法
          // 它主要是注册了一个`internalAutoProxyCreator`,但是若出现多次的话,这里不是覆盖的形式,而是以第一次的为主
          // 当然它内部有做等级的提升之类的,这个之前也有分析过~~~~
          AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
          // 看要不要强制使用CGLIB的方式(由此可以发现  这个属性若出现多次,是会是覆盖的形式)
          if ((Boolean) proxyTargetClass) {
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            return;
          }
        }
      }
    }
    // 如果一个都没有找到(我在想,肿么可能呢?)
    // 其实有可能:那就是自己注入这个类,而不是使用注解去注入(但并不建议这么去做)
    if (!candidateFound && logger.isInfoEnabled()) {
      // 输出info日志(注意并不是error日志)
    }
  }
}


这一步最重要的就是向Spring容器注入了一个自动代理创建器:org.springframework.aop.config.internalAutoProxyCreator,并且看看是采用CGLIB还是JDK代理。


跟踪AopConfigUtils的源码你会发现,事务这块向容器注入的是一个InfrastructureAdvisorAutoProxyCreator,它主要是读取Advisor类,并对符合的bean进行二次代理。在Spring AOP博文中有过详细介绍,这里略过~

参考:【小家Spring】Spring AOP的核心类:AbstractAdvisorAutoProxy自动代理创建器深度剖析(AnnotationAwareAspectJAutoProxyCreator)

相关文章
|
1月前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
|
1天前
|
Java 数据库连接 数据库
Spring基础3——AOP,事务管理
AOP简介、入门案例、工作流程、切入点表达式、环绕通知、通知获取参数或返回值或异常、事务管理
Spring基础3——AOP,事务管理
|
29天前
|
XML Java 数据库
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
这篇文章是Spring5框架的实战教程,详细介绍了事务的概念、ACID特性、事务操作的场景,并通过实际的银行转账示例,演示了Spring框架中声明式事务管理的实现,包括使用注解和XML配置两种方式,以及如何配置事务参数来控制事务的行为。
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
|
1月前
|
前端开发 Java 数据库连接
一天十道Java面试题----第五天(spring的事务传播机制------>mybatis的优缺点)
这篇文章总结了Java面试中的十个问题,包括Spring事务传播机制、Spring事务失效条件、Bean自动装配方式、Spring、Spring MVC和Spring Boot的区别、Spring MVC的工作流程和主要组件、Spring Boot的自动配置原理和Starter概念、嵌入式服务器的使用原因,以及MyBatis的优缺点。
|
13天前
|
Java Spring 开发者
掌握Spring事务管理,打造无缝数据交互——实用技巧大公开!
【8月更文挑战第31天】在企业应用开发中,确保数据一致性和完整性至关重要。Spring框架提供了强大的事务管理机制,包括`@Transactional`注解和编程式事务管理,简化了事务处理。本文深入探讨Spring事务管理的基础知识与高级技巧,涵盖隔离级别、传播行为、超时时间等设置,并介绍如何使用`TransactionTemplate`和`PlatformTransactionManager`进行编程式事务管理。通过合理设计事务范围和选择合适的隔离级别,可以显著提高应用的稳定性和性能。掌握这些技巧,有助于开发者更好地应对复杂业务需求,提升应用质量和可靠性。
25 0
|
3月前
|
XML Java 数据库
Spring5系列学习文章分享---第五篇(事务概念+特性+案例+注解声明式事务管理+参数详解 )
Spring5系列学习文章分享---第五篇(事务概念+特性+案例+注解声明式事务管理+参数详解 )
23 0
|
Java 数据库连接 API
请解释Spring中的声明式事务管理是如何工作的?
在Spring框架中,声明式事务管理是通过使用AOP(面向切面编程)和事务拦截器来实现的。声明式事务管理允许开发者通过在方法或类级别上添加注解来定义事务的行为,而无需显式地编写事务管理的代码。
请解释Spring中的声明式事务管理是如何工作的?
|
XML Java 测试技术
【Spring学习笔记 九】Spring声明式事务管理实现机制(下)
【Spring学习笔记 九】Spring声明式事务管理实现机制(下)
84 0
|
NoSQL Java 关系型数据库
【Spring学习笔记 九】Spring声明式事务管理实现机制
【Spring学习笔记 九】Spring声明式事务管理实现机制
67 0
|
Java 数据库 Spring
Spring学习(十一):声明式事务管理(响应式)
什么是事务:事务是数据库操作的最基本单元,逻辑上一组操作,要么都成功,如果有一个失败则意味着所有操作都失败
148 0
Spring学习(十一):声明式事务管理(响应式)