【小家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;
  }
}
相关文章
|
3月前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
|
2月前
|
Java 数据库连接 数据库
spring复习05,spring整合mybatis,声明式事务
这篇文章详细介绍了如何在Spring框架中整合MyBatis以及如何配置声明式事务。主要内容包括:在Maven项目中添加依赖、创建实体类和Mapper接口、配置MyBatis核心配置文件和映射文件、配置数据源、创建sqlSessionFactory和sqlSessionTemplate、实现Mapper接口、配置声明式事务以及测试使用。此外,还解释了声明式事务的传播行为、隔离级别、只读提示和事务超时期间等概念。
spring复习05,spring整合mybatis,声明式事务
|
2月前
|
Java 测试技术 数据库
Spring事务传播机制(最全示例)
在使用Spring框架进行开发时,`service`层的方法通常带有事务。本文详细探讨了Spring事务在多个方法间的传播机制,主要包括7种传播类型:`REQUIRED`、`SUPPORTS`、`MANDATORY`、`REQUIRES_NEW`、`NOT_SUPPORTED`、`NEVER` 和 `NESTED`。通过示例代码和数据库插入测试,逐一展示了每种类型的运作方式。例如,`REQUIRED`表示如果当前存在事务则加入该事务,否则创建新事务;`SUPPORTS`表示如果当前存在事务则加入,否则以非事务方式执行;`MANDATORY`表示必须在现有事务中运行,否则抛出异常;
113 4
Spring事务传播机制(最全示例)
|
28天前
|
Java 关系型数据库 MySQL
Spring事务失效,我总结了这7个主要原因
本文详细探讨了Spring事务在日常开发中常见的七个失效原因,包括数据库不支持事务、类不受Spring管理、事务方法非public、异常被捕获、`rollbackFor`属性配置错误、方法内部调用事务方法及事务传播属性使用不当。通过具体示例和源码分析,帮助开发者更好地理解和应用Spring事务机制,避免线上事故。适合所有使用Spring进行业务开发的工程师参考。
25 2
|
29天前
|
Java 程序员 Spring
Spring事务的1道面试题
每次聊起Spring事务,好像很熟悉,又好像很陌生。本篇通过一道面试题和一些实践,来拆解几个Spring事务的常见坑点。
Spring事务的1道面试题
|
2月前
|
Java Spring
Spring 事务传播机制是什么?
Spring 事务传播机制是什么?
22 4
|
1月前
|
监控 Java 数据库
Spring事务中的@Transactional注解剖析
通过上述分析,可以看到 `@Transactional`注解在Spring框架中扮演着关键角色,它简化了事务管理的复杂度,让开发者能够更加专注于业务逻辑本身。合理运用并理解其背后的机制,对于构建稳定、高效的Java企业应用至关重要。
41 0
|
3月前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
41 0
Spring高手之路22——AOP切面类的封装与解析
|
3月前
|
XML Java 数据库
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
这篇文章是Spring5框架的实战教程,详细介绍了事务的概念、ACID特性、事务操作的场景,并通过实际的银行转账示例,演示了Spring框架中声明式事务管理的实现,包括使用注解和XML配置两种方式,以及如何配置事务参数来控制事务的行为。
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
|
3月前
|
前端开发 Java 数据库连接
一天十道Java面试题----第五天(spring的事务传播机制------>mybatis的优缺点)
这篇文章总结了Java面试中的十个问题,包括Spring事务传播机制、Spring事务失效条件、Bean自动装配方式、Spring、Spring MVC和Spring Boot的区别、Spring MVC的工作流程和主要组件、Spring Boot的自动配置原理和Starter概念、嵌入式服务器的使用原因,以及MyBatis的优缺点。