初步走入 Spring 事务底层核心前置源码(上)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 初步走入 Spring 事务底层核心前置源码

前言

事务从 AOP 开始,有了 AOP 基础就可以学习 Spring 事务阶段了

事务加载、对象创建

一切从配置文件开始,提前把 BeanDefinition 定义信息准备好,到达实例化阶段再统一处理

代码

jdbc.username=root
jdbc.password=123456
jdbc.url=jdbc:mysql://localhost:3306/demo
jdbc.driverClassName=com.mysql.jdbc.Driver
<context:property-placeholder location="classpath:dbconfig.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  <property name="username" value="${jdbc.username}"/>
  <property name="password" value="${jdbc.password}"/>
  <property name="url" value="${jdbc.url}"/>
  <property name="driverClassName" value="${jdbc.driverClassName}"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
  <constructor-arg name="dataSource" ref="dataSource"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>
<bean id="bookService" class="com.vnjohn.tx.xml.service.BookService">
  <property name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookDao" class="com.vnjohn.tx.xml.dao.BookDao">
  <property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<aop:config>
  <aop:pointcut id="txPoint" expression="execution(* com.vnjohn.tx.xml.*.*.*(..))"/>
  <aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"/>
</aop:config>
<tx:advice id="myAdvice" transaction-manager="transactionManager">
  <tx:attributes>
    <tx:method name="checkout" propagation="REQUIRED"/>
    <tx:method name="updateStock" propagation="REQUIRES_NEW"/>
  </tx:attributes>
</tx:advice>

${} 读取本地资源文件的变量,核心类是 PropertySourcesPlaceholderConfigurer,只要 XML 配置了 <context:property-placeholder> 标签元素,先会创建该类的 beanDefinition 信息,后会再去执行 BFPP 时先将这个类进行创建,然后执行 postProcessBeanFactory 方法进行资源文件的加载,加载完所有的资源文件以后,再对所有时用到占位符读取变量的地方进行赋值操作.

准备 BeanDefinition 信息

AopNamespaceHandler 执行器对应的解析类是 ConfigBeanDefinitionParser,TxNamespaceHandler 执行器对应的解析类是 TxAdviceBeanDefinitionParser

解析 <aop:config> 标签时,先会注册自动代理创建器,后续会用它来操作生成类的代理对象,接着会解析其下的子标签

// 解析aop:config子节点下的aop:pointcut/aop:advice/aop:aspect
List<Element> childElts = DomUtils.getChildElements(element);
for (Element elt: childElts) {
  String localName = parserContext.getDelegate().getLocalName(elt);
  if (POINTCUT.equals(localName)) {
    parsePointcut(elt, parserContext);
  }
  else if (ADVISOR.equals(localName)) {
    parseAdvisor(elt, parserContext);
  }
  else if (ASPECT.equals(localName)) {
    parseAspect(elt, parserContext);
  }
}

在上述的案例配置文件中,有两个标签,<aop:pointcut> 会创建表达式切入点的定义信息:AspectJExpressionPointcut,会在其信息下追加 expression 字符串用于后面的类匹配和方法匹配、<aop:advisor> 创建 advice 持有者定义信息:DefaultBeanFactoryPointcutAdvisor,会其下信息设置 adviceBeanName 属性、pointcut-ref 属性,这两者都是字符串会在后续加载具体的 Bean

解析 <tx:advice> 标签时,会先执行父类 AbstractSingleBeanDefinitionParser#parseInternal 方法去构建一个最外层类型为 TransactionInterceptor 类型的定义信息,调用子类 TxAdviceBeanDefinitionParser#doParse 方法解析其属性和子节点信息,首先设置 transactionManager 属性值,如果有配置其属性就取设置的,没有取默认的 transaction-manager;随后判断其下的 <tx:attribute> 子节点「类型为 NameMatchTransactionAttributeSource」,超过一个会记录错误日志信息,再获取 <tx:attributes> 标签下所有的 <tx:method> 节点,每一个节点都作为 「RuleBasedTransactionAttribute 类型」& 设置该节点所配置的属性,最后所有的 <tx:method> 定义信息作为 value,key 为 nameMap 进行存储;nameMap 又作为 NameMatchTransactionAttributeSource 类型的一个属性.

class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
  // ...... 
  @Override
  protected Class<?> getBeanClass(Element element) {
    return TransactionInterceptor.class;
  }
}

tx:method 标签可以配置以下几个属性👇

  • 传播机制:propagation 默认值(REQUIRED)
  • 隔离级别:isolation 默认值(DEFAULT)
  • 超时时间:timeout 默认值(-1 不超时)
  • 是否只读:readOnly 默认值(false)

从以上流程图和文字描述来看,大部分都是需要待解析的属性,在实例化 bean 时会对这些类的属性进行具体的解析,解析整个 <tx:advice> 标签后,类的结构如下图所示👇

创建实例处理过程

依然按照以上的 xml 配置文件内容来进行流程讲解;加载完所有 Bean 定义信息,进行实例化过程

  • 普通 Bean【非内部的类型,比如 Advice、Pointcut、Advisor、AopInfrastructureBean】在执行时会触发 AbstractAutoProxyCreator 类调用 postProcessBeforeInstantiation(实例化之前的方法)),此时会将所有的 advisor 全部加载好存入缓存
  • 实例化完 bookService 以后,填充属性阶段,属性类型为 RunTimeBeanReference,接着进行 bookDao 实例化流程,实例化完 bookDao 以后,填充属性阶段,设置好相关的数据
  • 接着调用 AbstractAutoProxyCreator#postProcessAfterInitialization(初始化之后的方法)
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        // 获取当前bean的key:如果beanName不为空,则以beanName为key,如果为FactoryBean类型,
        // 前面还会添加&符号,如果beanName为空,则以当前bean对应的class为key
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // 判断当前bean是否正在被代理,如果正在被代理则不进行封装
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            // 如果它需要被代理,则需要封装指定的bean
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

前面所有的工作处理完以后,剩下的就是创建代理对象了,这里面涉及到会去创建具体的事务 advisor 增强器

  • 先将所有符合条件的 advisor 查找出来,再对其进行匹配,验证类、方法是否能够适配
  • 提供 hook 钩子方法用于对目标 advisor 进行扩展,会通过 advisor 持有者去加载其下的 advice 增强器,如果在一级缓存中获取不到该对象,那么就会走一个整体的创建 bean 过程,adviceBeanName:myPoint 这个 advice 对应的类型是 TransactionInterceptor ,之前在加载 beanDefinition 信息时它下面是存在两个属性的,实例化完、填充属性阶段将其补充后这个事务相关的 advice 就创建好了
  • 当所有的 advice 加载好以后,再添加一个新的 advice:ExposeInvocationInterceptor,将它置于首位,标为 0
  • 对 advice 进行拓扑排序后,接下来就是创建真正代理对象的过程了,这里不做过多阐述,如果是 CGLIB 通过 enhancer.create 进行创建,JDK 就通过 Proxy.newProxyInstance 进行创建.

目录
相关文章
|
15天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
45 2
|
1月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
23天前
|
Java 开发者 Spring
Spring高手之路24——事务类型及传播行为实战指南
本篇文章深入探讨了Spring中的事务管理,特别是事务传播行为(如REQUIRES_NEW和NESTED)的应用与区别。通过详实的示例和优化的时序图,全面解析如何在实际项目中使用这些高级事务控制技巧,以提升开发者的Spring事务管理能力。
39 1
Spring高手之路24——事务类型及传播行为实战指南
|
17天前
|
XML Java 数据库连接
Spring中的事务是如何实现的
Spring中的事务管理机制通过一系列强大的功能和灵活的配置选项,为开发者提供了高效且可靠的事务处理手段。无论是通过注解还是AOP配置,Spring都能轻松实现复杂的事务管理需求。掌握这些工具和最佳实践,能
30 3
|
21天前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
45 9
|
2月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
120 5
|
2月前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
2月前
|
XML Java 数据格式
手动开发-简单的Spring基于注解配置的程序--源码解析
手动开发-简单的Spring基于注解配置的程序--源码解析
47 0
|
3月前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
187 2