【小家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;
  }
}
相关文章
|
1月前
|
XML 存储 Java
Spring重要类解析
Spring重要类解析
32 0
|
9天前
|
消息中间件 资源调度 Java
实时计算 Flink版产品使用问题之拉取代码没有这个类,但是在下载的jar包中有这个类,是什么导致的
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
1月前
|
Java 关系型数据库 Docker
docker打包部署spring boot应用(mysql+jar+Nginx)
docker打包部署spring boot应用(mysql+jar+Nginx)
|
1月前
|
安全 Java Docker
|
1月前
|
设计模式 Java 数据库连接
【Spring源码】JDBC数据源访问实现
我们再来看看阅读线索三,这方面我们从设计模式进行入手。阅读线索三:从这个模块可以学到什么我们看下以下代码,PreparedStatement实例的是由PreparedStatementCreator实现的。再来看看PreparedStatementCreator接口,一共有三个子类实现。也就是说PreparedStatement的三种不同实现被封装到三个子类中,而具体需要哪种实现,只需要传入不同。
45 1
【Spring源码】JDBC数据源访问实现
|
1月前
|
Java Maven Spring
maven打包插件maven-jar-plugin与spring-boot-maven-plugin
该内容介绍了两个Maven打包插件:`spring-boot-maven-plugin`和`maven-jar-plugin`。`spring-boot-maven-plugin`是Spring Boot项目的默认打包工具,它会包含项目类文件、资源和依赖的jar,但不会解编译依赖。而`maven-jar-plugin`则用于创建普通JAR包,不包含依赖。文中还展示了两个插件打包后的效果差异,并强调了持续练习以掌握这些技能的重要性。
35 0
|
1月前
|
消息中间件 安全 Java
在Spring Bean中,如何通过Java配置类定义Bean?
【4月更文挑战第30天】在Spring Bean中,如何通过Java配置类定义Bean?
27 1
|
1月前
|
SQL Java 数据库连接
Springboot框架整合Spring JDBC操作数据
JDBC是Java数据库连接API,用于执行SQL并访问多种关系数据库。它包括一系列Java类和接口,用于建立数据库连接、创建数据库操作对象、定义SQL语句、执行操作并处理结果集。直接使用JDBC涉及七个步骤,包括加载驱动、建立连接、创建对象、定义SQL、执行操作、处理结果和关闭资源。Spring Boot的`spring-boot-starter-jdbc`简化了这些步骤,提供了一个在Spring生态中更便捷使用JDBC的封装。集成Spring JDBC需要添加相关依赖,配置数据库连接信息,并通过JdbcTemplate进行数据库操作,如插入、更新、删除和查询。
|
1月前
|
安全 Java 测试技术
Spring Boot 自动化单元测试类的编写过程
企业开发不仅要保障业务层与数据层的功能安全有效,也要保障表现层的功能正常。但是我们一般对表现层的测试都是通过postman手工测试的,并没有在打包过程中代码体现表现层功能被测试通过。那么能否在测试用例中对表现层进行功能测试呢?答案是可以的,我们可以使用MockMvc来实现它。
69 0
|
1月前
|
Java Spring
使用JDBCTemplate实现与Spring结合,方法公用 ——共用实现类(BaseImpl)
使用JDBCTemplate实现与Spring结合,方法公用 ——共用实现类(BaseImpl)
13 1

热门文章

最新文章