【小家Spring】Spring事务相关的基础类打点(spring-jdbc和spring-tx两个jar),着重讲解AnnotationTransactionAttributeSource(中)

简介: 【小家Spring】Spring事务相关的基础类打点(spring-jdbc和spring-tx两个jar),着重讲解AnnotationTransactionAttributeSource(中)

MatchAlwaysTransactionAttributeSource


它是TransactionAttributeSource的一个最简单的实现,每次调用,都是返回相同的TransactionAttribute


当然它是可议set一个TransactionAttribute作为通用的事务属性的实现的


AnnotationTransactionAttributeSource

这个就是重点了,它是基于注解驱动的事务管理的事务属性源,和@Transaction相关,也是现在使用得最最多的方式。


它的基本作用为:它遇上比如@Transaction标注的方法时,此类会分析此事务注解,最终组织形成一个TransactionAttribute供随后的调用。

public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable {
  // 这个是“向下兼容”,JavaEE提供的其余两种注解~~
  private static final boolean jta12Present; //JTA 1.2事务注解
  private static final boolean ejb3Present; //EJB 3 事务注解是
  static {
    ClassLoader classLoader = AnnotationTransactionAttributeSource.class.getClassLoader();
    jta12Present = ClassUtils.isPresent("javax.transaction.Transactional", classLoader);
    ejb3Present = ClassUtils.isPresent("javax.ejb.TransactionAttribute", classLoader);
  }
  // true:只处理public方法(基于JDK的代理  显然就只会处理这种方法)
  // false:private/protected等方法都会处理。   基于AspectJ代理得方式可议设置为false
  // 默认情况下:会被赋值为true,表示只处理public的方法
  private final boolean publicMethodsOnly;
  // 保存用于分析事务注解的事务注解分析器   这个注解分析的解析器是重点
  private final Set<TransactionAnnotationParser> annotationParsers;
  // 构造函数, publicMethodsOnly 缺省使用 true
  public AnnotationTransactionAttributeSource() {
    this(true);
  }
  public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
    this.publicMethodsOnly = publicMethodsOnly;
    if (jta12Present || ejb3Present) {
      this.annotationParsers = new LinkedHashSet<>(4);
      this.annotationParsers.add(new SpringTransactionAnnotationParser());
      if (jta12Present) {
        this.annotationParsers.add(new JtaTransactionAnnotationParser());
      }
      if (ejb3Present) {
        this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
      }
    } 
    // 默认情况下,只添加Spring自己的注解解析器(绝大部分情况都实这里)
    else {
      this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());
    }
  }
  // 自己也可以指定一个TransactionAnnotationParser   或者多个也成
  public AnnotationTransactionAttributeSource(TransactionAnnotationParser annotationParser) { ... }
  public AnnotationTransactionAttributeSource(TransactionAnnotationParser... annotationParsers) { ... }
  public AnnotationTransactionAttributeSource(Set<TransactionAnnotationParser> annotationParsers) { ... }
  // 获取某个类/方法上的事务注解属性(属于 父类的抽象方法)
  @Override
  @Nullable
  protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
    return determineTransactionAttribute(clazz);
  }
  @Override
  @Nullable
  protected TransactionAttribute findTransactionAttribute(Method method) {
    return determineTransactionAttribute(method);
  }
  // 具体实现如下:
  // 分析获取某个被注解的元素(AnnotatedElement ),具体的来讲,指的是一个类或者一个方法上的事务注解属性。
  // 实现会遍历自己属性annotationParsers中所包含的事务注解属性分析器试图获取事务注解属性  所以主要还是依赖于TransactionAnnotationParser 去解析的
  @Nullable
  protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
    for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
      TransactionAttribute attr = annotationParser.parseTransactionAnnotation(element);
      if (attr != null) {
        return attr;
      }
    }
    return null;
  }
  /**
   * By default, only public methods can be made transactional.
   */
  @Override
  protected boolean allowPublicMethodsOnly() {
    return this.publicMethodsOnly;
  }
  ...
}


从源码中可议知道,真正提供给调用的getTransactionAttribute在父类中实现的:


AbstractFallbackTransactionAttributeSource


AbstractFallbackTransactionAttributeSource是接口TransactionAttributeSource的抽象实现,也是上面提到的工具类AnnotationTransactionAttributeSource的父类。

public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource {
  // 针对没有事务注解属性的方法进行事务注解属性缓存时使用的特殊值,用于标记该方法没有事务注解属性
  // 从而不用在首次缓存在信息后,不用再次重复执行真正的分析  来提高查找的效率
  // 标注了@Transaction注解的表示有事务属性的,才会最终加入事务。但是,但是此处需要注意的是,只要被事务的Advisor切中的,都会缓存起来  放置过度的查找~~~~ 因此才有这个常量的出现
  private static final TransactionAttribute NULL_TRANSACTION_ATTRIBUTE = new DefaultTransactionAttribute() {
    @Override
    public String toString() {
      return "null";
    }
  };
  // 方法上的事务注解属性缓存,key使用目标类上的方法,使用类型MethodClassKey来表示
  // 这个Map会比较大,会被事务相关的Advisor拦截下来的方法,最终都会缓存下来。关于事务相关的Advisor,后续也是会着重讲解的~~~
  // 因为会有很多,所以我们才需要一个NULL_TRANSACTION_ATTRIBUTE常量来提高查找的效率~~~
  private final Map<Object, TransactionAttribute> attributeCache = new ConcurrentHashMap<>(1024);
  // 获取指定方法上的注解事务属性   如果方法上没有注解事务属性,则使用目标方法所属类上的注解事务属性
  @Override
  @Nullable
  public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    // 如果目标方法是内置类Object上的方法,总是返回null,这些方法上不应用事务
    if (method.getDeclaringClass() == Object.class) {
      return null;
    }
    // 先看缓存里有木有,此处使用的非常经典的MethodClassKey作为Map的key
    Object cacheKey = getCacheKey(method, targetClass);
    TransactionAttribute cached = this.attributeCache.get(cacheKey);
    if (cached != null) {
      //目标方法上上并没有事务注解属性,但是已经被尝试分析过并且已经被缓存,
      // 使用的值是 NULL_TRANSACTION_ATTRIBUTE,所以这里再次尝试获取其注解事务属性时,直接返回 null
      if (cached == NULL_TRANSACTION_ATTRIBUTE) {
        return null;
      } else {
        return cached;
      }
    }
    // 缓存没有命中~~~~
    else {
      // 通过方法、目标Class 分析出此方法上的事务属性~~~~~
      TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
      // 如果目标方法上并没有使用注解事务属性,也缓存该信息,只不过使用的值是一个特殊值:
      if (txAttr == null) {
        this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
      }
      // 存在目标属性~ 就put到里面去。
      // 获取到methodIdentification  基本只为了输出日志~~~
      else {
        String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
        if (txAttr instanceof DefaultTransactionAttribute) {
          ((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);
        }
        if (logger.isTraceEnabled()) {
          logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
        }
        this.attributeCache.put(cacheKey, txAttr);
      }
      return txAttr;
    }
  }
  //查找目标方法上的事务注解属性 也是上面的核心方法
  @Nullable
  protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    // 如果事务注解属性分析仅仅针对public方法,而当前方法不是public,则直接返回null
    // 如果是private,AOP是能切入,代理对象也会生成的  但就是事务不回生效的~~~~
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
      return null;
    }
    // 上面说了,因为Method并不一样属于目标类。所以这个方法就是获取targetClass上的那个和method对应的方法  也就是最终要执行的方法
    Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
    // 第一步:去找直接标记在方法上的事务属性~~~ 如果方法上有就直接返回(不用再看类上的了)
    // findTransactionAttribute这个方法其实就是子类去实现的
    TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
    if (txAttr != null) {
      return txAttr;
    }
    // 然后尝试检查事务注解属性是否标记在目标方法 specificMethod(注意此处用不是Method) 所属类上
    txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
    if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
      return txAttr;
    }
    // 程序走到这里说明目标方法specificMethod,也就是实现类上的目标方法上没有标记事务注解属性(否则直接返回了嘛)
    // 如果 specificMethod 和 method 不同,则说明 specificMethod 是具体实现类的方法method 是实现类所实现接口的方法
    // 因此再次尝试从 method 上获取事务注解属性
    // 这也就是为何我们的@Transaction标注在接口上或者接口的方法上都是好使的原因~~~~~~~
    if (specificMethod != method) {
      // Fallback is to look at the original method.
      txAttr = findTransactionAttribute(method);
      if (txAttr != null) {
        return txAttr;
      }
      txAttr = findTransactionAttribute(method.getDeclaringClass());
      if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
        return txAttr;
      }
    }
    return null;
  }
  // 可议看到默认值是false  表示private的也是ok的
  // 但是`AnnotationTransactionAttributeSource`复写了它  可以由开发者指定(默认是true了)
  protected boolean allowPublicMethodsOnly() {
    return false;
  }
}
相关文章
|
8月前
|
SQL Java 关系型数据库
Spring事务传播机制:7种姿势教你玩转"事务接力赛"
事务传播机制是Spring框架中用于管理事务行为的重要概念,它决定了在方法调用时事务如何传递与执行。通过7种传播行为,开发者可以灵活控制事务边界,适应不同业务场景。例如:REQUIRED默认加入或新建事务,REQUIRES_NEW独立开启新事务,NESTED支持嵌套回滚等。合理使用传播机制不仅能保障数据一致性,还能提升系统性能与健壮性。掌握这“七种人格”,才能在复杂业务中游刃有余。
|
Java Spring
Spring中事务失效的场景
因为Spring事务是基于代理来实现的,所以某个加了@Transactional的⽅法只有是被代理对象调⽤时, 那么这个注解才会⽣效 , 如果使用的是被代理对象调用, 那么@Transactional会失效 同时如果某个⽅法是private的,那么@Transactional也会失效,因为底层cglib是基于⽗⼦类来实现 的,⼦类是不能重载⽗类的private⽅法的,所以⽆法很好的利⽤代理,也会导致@Transactianal失效 如果在业务中对异常进行了捕获处理 , 出现异常后Spring框架无法感知到异常, @Transactional也会失效
|
Java 关系型数据库 数据库
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——常见问题总结
本文总结了Spring Boot中使用事务的常见问题,虽然通过`@Transactional`注解可以轻松实现事务管理,但在实际项目中仍有许多潜在坑点。文章详细分析了三个典型问题:1) 异常未被捕获导致事务未回滚,需明确指定`rollbackFor`属性;2) 异常被try-catch“吃掉”,应避免在事务方法中直接处理异常;3) 事务范围与锁范围不一致引发并发问题,建议调整锁策略以覆盖事务范围。这些问题看似简单,但一旦发生,排查难度较大,因此开发时需格外留意。最后,文章提供了课程源代码下载地址,供读者实践参考。
387 0
|
Java 关系型数据库 数据库
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——Spring Boot 事务配置
本文介绍了 Spring Boot 中的事务配置与使用方法。首先需要导入 MySQL 依赖,Spring Boot 会自动注入 `DataSourceTransactionManager`,无需额外配置即可通过 `@Transactional` 注解实现事务管理。接着通过创建一个用户插入功能的示例,展示了如何在 Service 层手动抛出异常以测试事务回滚机制。测试结果表明,数据库中未新增记录,证明事务已成功回滚。此过程简单高效,适合日常开发需求。
1765 0
|
Java 数据库 微服务
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——事务相关
本文介绍Spring Boot事务配置管理,阐述事务在企业应用开发中的重要性。事务确保数据操作可靠,任一异常均可回滚至初始状态,如转账、购票等场景需全流程执行成功才算完成。同时,事务管理在Spring Boot的service层广泛应用,但根据实际需求也可能存在无需事务的情况,例如独立数据插入操作。
336 0
|
9月前
|
Java 关系型数据库 数据库
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
在Java开发中,Spring框架通过事务管理机制,帮我们轻松实现了这种“承诺”。它不仅封装了底层复杂的事务控制逻辑(比如手动开启、提交、回滚事务),还提供了灵活的配置方式,让开发者能专注于业务逻辑,而不用纠结于事务细节。
1123 1
|
12月前
|
人工智能 Java 数据库连接
Spring事务失效场景
本文深入探讨了Spring框架中事务管理可能失效的几种常见场景及解决方案,包括事务方法访问级别不当、方法内部自调用、错误的异常处理、事务管理器或数据源配置错误、数据库不支持事务以及不合理的事务传播行为或隔离级别。通过合理配置和正确使用`@Transactional`注解,开发者可以有效避免这些问题,确保应用的数据一致性和完整性。
1004 10
|
11月前
|
Java 关系型数据库 MySQL
【Spring】【事务】初学者直呼学会了的Spring事务入门
本文深入解析了Spring事务的核心概念与使用方法。Spring事务是一种数据库事务管理机制,通过确保操作的原子性、一致性、隔离性和持久性(ACID),维护数据完整性。文章详细讲解了声明式事务(@Transactional注解)和编程式事务(TransactionTemplate、PlatformTransactionManager)的区别与用法,并探讨了事务传播行为(如REQUIRED、REQUIRES_NEW等)及隔离级别(如READ_COMMITTED、REPEATABLE_READ)。
754 1
|
SQL Java 数据库连接
Spring中的事务是如何实现的
1. Spring事务底层是基于数据库事务和AOP机制的 2. ⾸先对于使⽤了@Transactional注解的Bean,Spring会创建⼀个代理对象作为Bean 3. 当调⽤代理对象的⽅法时,会先判断该⽅法上是否加了@Transactional注解 4. 如果加了,那么则利⽤事务管理器创建⼀个数据库连接 5. 并且修改数据库连接的autocommit属性为false,禁⽌此连接的⾃动提交,这是实现Spring事务⾮ 常重要的⼀步 6. 然后执⾏当前⽅法,⽅法中会执⾏sql 7. 执⾏完当前⽅法后,如果没有出现异常就直接提交事务 8. 如果出现了异常,并且这个异常是需要回滚的就会回滚事务
|
JavaScript Java 开发者
Spring事务失效,常见的情况有哪些?
本文总结了Spring事务失效的7种常见情况,包括未启用事务管理功能、方法非public类型、数据源未配置事务管理器、自身调用问题、异常类型错误、异常被吞以及业务和事务代码不在同一线程中。同时提供了两种快速定位事务相关Bug的方法:通过查看日志(设置为debug模式)或调试代码(在TransactionInterceptor的invoke方法中设置断点)。文章帮助开发者更好地理解和解决Spring事务中的问题。
675 7