第三十一章 Spring之假如让你来写事务——融入IOC容器篇

Spring源码阅读目录

第一部分——IOC篇

第一章 Spring之最熟悉的陌生人——IOC
第二章 Spring之假如让你来写IOC容器——加载资源篇
第三章 Spring之假如让你来写IOC容器——解析配置文件篇
第四章 Spring之假如让你来写IOC容器——XML配置文件篇
第五章 Spring之假如让你来写IOC容器——BeanFactory和FactoryBean
第六章 Spring之假如让你来写IOC容器——Scope和属性填充
第七章 Spring之假如让你来写IOC容器——属性填充特别篇:SpEL表达式
第八章 Spring之假如让你来写IOC容器——拓展篇
第九章 Spring之源码阅读——环境搭建篇
第十章 Spring之源码阅读——IOC篇

第二部分——AOP篇

第十一章 Spring之不太熟的熟人——AOP
第十二章 Spring之不得不了解的内容——概念篇
第十三章 Spring之假如让你来写AOP——AOP联盟篇
第十四章 Spring之假如让你来写AOP——雏形篇
第十五章 Spring之假如让你来写AOP——Joinpoint(连接点)篇
第十六章 Spring之假如让你来写AOP——Pointcut(切点)篇
第十七章 Spring之假如让你来写AOP——Advice(通知)上篇
第十八章 Spring之假如让你来写AOP——Advice(通知)下篇
第十九章 Spring之假如让你来写AOP——番外篇:Spring早期设计
第二十章 Spring之假如让你来写AOP——Aspect(切面)篇
第二十一章 Spring之假如让你来写AOP——Weaver(织入器)篇
第二十二章 Spring之假如让你来写AOP——Target Object(目标对象)篇
第二十三章 Spring之假如让你来写AOP——融入IOC容器篇
第二十四章 Spring之源码阅读——AOP篇

第三部分——事务篇

第二十五章 Spring之曾经的老朋友——事务
第二十六章 Spring之假如让你来写事务——初稿篇
第二十七章 Spring之假如让你来写事务——铁三角篇
第二十八章 Spring之假如让你来写事务——属性篇
第二十九章 Spring之假如让你来写事务——状态篇
第三十章 Spring之假如让你来写事务——管理篇
第三十一章 Spring之假如让你来写事务——融入IOC容器篇
第三十二章 Spring之源码阅读——事务篇


文章目录

  • Spring源码阅读目录
    • 第一部分——IOC篇
    • 第二部分——AOP篇
    • 第三部分——事务篇
  • 前言
  • 尝试动手写IOC容器
      • 第二十九版 融入IOC容器
  • 总结


前言

    对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
在这里插入图片描述

    所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》


    书接上回,在上篇 第三十章 Spring之假如让你来写事务——管理篇 中,A君 实现了 事务 全部功能了。接下来看看 A君 会有什么骚操作吧

尝试动手写IOC容器

    出场人物:A君(苦逼的开发)、老大(项目经理)

    背景:老大要求A君在一周内开发个简单的 IOC容器

    前情提要:A君 实现了 事务 全部功能了 。。。

第二十九版 融入IOC容器

    “可以啊,A君,干的不错,感觉是不是就是顺手的事?” 老大 看着代码,笑着说道

    “还行吧,整体逻辑是还好,只是要去了解 事务 的一些知识,以及考虑对其他框架的内容比较麻烦。” A君 忍不住抱怨到

    “一个成功的框架必然要对主流框架进行兼容,不然就流行不起来了。” 老大 语重心长的说到 “接下来,你还要考虑一件事!”

    “啥,这还不够吗?还有考虑哪里” A君 愕然

    “事务 方面是足够了,但是呢,配置太过繁琐了,简单一点。” 老大

    “好趴。” 反正都到这一步了,也不差这一步了。A君 自我安慰道

    怎么和 IOC 融合,在前面做 AOP 的时候,A君 已经有经验了。只需要把 XML 解析成对应的 BeanDefinition 即可。老样子,A君 首先定义 xsd 文件,这个都是一样的,直接从 Spring 中拷贝就行了。接下来,就是解析器了,由于这是拓展标签,所以没有相应的解析器,需要自己实现。A君 先看下标签,如下:

   <!-- 配置事务的建议 --><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="save*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/><tx:method name="update*" propagation="NESTED" rollback-for="java.lang.Exception"/><tx:method name="delete*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/></tx:attributes></tx:advice>

总共算下来就三个标签,tx:advicetx:attributestx:method。逐个解析并注册就行了。A君 先定义 TxAdviceBeanDefinitionParser 类,重写 parse 方法,代码如下:

	@Overridepublic BeanDefinition parse(Element element, ParserContext parserContext) {String defaultTMName = "transactionManager";/*** 定义TransactionInterceptor*/BeanDefinition ti = new BeanDefinition(TransactionInterceptor.class);String adviceId = element.getAttribute(ADVICE_ID);ti.setId(adviceId);parserContext.registerBeanDefinition(adviceId, ti);/*** 事务管理器*/String transactionManagerName = TxNamespaceHandler.getTransactionManagerName(element);ti.getProperties().add(defaultTMName, new RuntimeBeanReference(transactionManagerName));List<Element> txAttributes = DomUtils.getChildElementsByTagName(element, ATTRIBUTES_ELEMENT);if (txAttributes.size() == 1) {Element attributeSourceElement = txAttributes.get(0);BeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext);ti.getProperties().add("transactionAttributeSource", new RuntimeBeanReference(attributeSourceDefinition.getId()));ti.getProperties().add("transactionManagerBeanName", new RuntimeBeanNameReference(defaultTMName));} else {//TODO}return ti;}

tx:advice 对应着方法拦截器,而拦截器有依赖于 transactionAttributeSourcetransactionManager 这两个属性,transactionManager 可以直接从当前标签里边取,而 transactionAttributeSource 则需要解析 tx:attributes 标签。tx:attributes 则是对应的回滚规则,于是乎,A君 趁热打铁,继续实现解析 tx:attributes。新增 parseAttributeSource 方法。代码如下:

private BeanDefinition parseAttributeSource(Element attrEle, ParserContext parserContext) {/*** 获取method标签*/List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, METHOD_ELEMENT);ManagedMap<TypedStringValue, RuleBasedTransactionAttribute> transactionAttributeMap =new ManagedMap<>(methods.size());for (Element methodEle : methods) {//解析方法名String name = methodEle.getAttribute(METHOD_NAME_ATTRIBUTE);TypedStringValue nameHolder = new TypedStringValue(name);/*** 解析事务传播行为* 解析事务超时时间* 解析事务隔离级别* 解析是否只读*/RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute();String propagation = methodEle.getAttribute(PROPAGATION_ATTRIBUTE);String isolation = methodEle.getAttribute(ISOLATION_ATTRIBUTE);String timeout = methodEle.getAttribute(TIMEOUT_ATTRIBUTE);String readOnly = methodEle.getAttribute(READ_ONLY_ATTRIBUTE);if (StringUtils.isNotBlank(propagation)) {attribute.setPropagationBehaviorName(RuleBasedTransactionAttribute.PREFIX_PROPAGATION + propagation);}if (StringUtils.isNotBlank(isolation)) {attribute.setIsolationLevelName(RuleBasedTransactionAttribute.PREFIX_ISOLATION + isolation);}if (StringUtils.isNotBlank(timeout)) {attribute.setTimeoutString(timeout);}if (StringUtils.isNotBlank(readOnly)) {attribute.setReadOnly(Boolean.parseBoolean(methodEle.getAttribute(READ_ONLY_ATTRIBUTE)));}List<RollbackRuleAttribute> rollbackRules = new ArrayList<>(1);if (methodEle.hasAttribute(ROLLBACK_FOR_ATTRIBUTE)) {String rollbackForValue = methodEle.getAttribute(ROLLBACK_FOR_ATTRIBUTE);addRollbackRuleAttributesTo(rollbackRules, rollbackForValue);}if (methodEle.hasAttribute(NO_ROLLBACK_FOR_ATTRIBUTE)) {String noRollbackForValue = methodEle.getAttribute(NO_ROLLBACK_FOR_ATTRIBUTE);addNoRollbackRuleAttributesTo(rollbackRules, noRollbackForValue);}attribute.setRollbackRules(rollbackRules);transactionAttributeMap.put(nameHolder, attribute);}BeanDefinition attributeSourceDefinition = new BeanDefinition(NameMatchTransactionAttributeSource.class);attributeSourceDefinition.getPropertyValues().add("nameMap", transactionAttributeMap);parserContext.registerWithGeneratedName(attributeSourceDefinition);return attributeSourceDefinition;

整体代码没什么难度,解析对应的标签,并且添加到对应的对象中即可。解析器定义完成之后,还需要注册解析器,需要继承 NamespaceHandlerSupportA君 定义 TxNamespaceHandler 类,代码如下:

public class TxNamespaceHandler extends NamespaceHandlerSupport {static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";static String getTransactionManagerName(Element element) {return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);}@Overridepublic void init() {registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());}}

注册器有了,还要让 容器 只要有这么个玩意,A君 修改 spring.handlers 文件,把对应的注册器注册进去

http\://www.springframework.org/schema/aop=com.hqd.ch03.v29.aop.config.AopNamespaceHandler
http\://www.springframework.org/schema/tx=com.hqd.ch03.v29.tx.transaction.config.TxNamespaceHandler

好了,就这些东西了,接下来又到了快乐的测试环节了,整体代码没什么变化,主要是配置文件变了,A君 定义 bean,xml 文件,配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xmlns="http://www.springframework.org/schema/beans"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/spring?useSSL=false&amp;serverTimezone=UTC"/><property name="username" value="root"/><property name="password" value="root"/><property name="initialSize" value="5"/><property name="minIdle" value="5"/><property name="maxActive" value="20"/><property name="validationQuery" value="SELECT 1"/><property name="testWhileIdle" value="true"/><property name="testOnBorrow" value="true"/><property name="testOnReturn" value="false"/></bean><bean id="jdbcTemplate" class="com.hqd.ch03.v29.jdbc.core.SimpleJdbcTemplate"><property name="dataSource" ref="dataSource"/></bean><bean id="userService" class="com.hqd.ch03.bean.jdbc.v29.UserServiceImplV29"><property name="jdbcTemplate" ref="jdbcTemplate"/></bean><bean id="transactionManager" class="com.hqd.ch03.v29.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><aop:config expose-proxy="true"><aop:pointcut id="txPoint" expression="execution(* com.hqd.ch03.bean.jdbc.v29.UserServiceImplV29.*Data(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/></aop:config><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="save*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/><tx:method name="update*" propagation="NESTED" rollback-for="java.lang.Exception"/><tx:method name="delete*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/></tx:attributes></tx:advice>
</beans>

相较于之前,这个配置明显简单了许多。有了配置文件,代码有简单了。测试代码如下:

	@Testpublic void v29() throws Throwable {System.out.println("############# 第二十九版: 融入IOC篇 #############");com.hqd.ch03.v29.AbstractSpringImitation beanFactory =new com.hqd.ch03.v29.SpringImitationXml("classpath:v29/bean.xml");UserServiceImplV29 userService = (UserServiceImplV29) beanFactory.getBean("userService");System.out.println(userService);com.hqd.ch03.bean.jdbc.User user = new com.hqd.ch03.bean.jdbc.User();user.setId(1);user.setSex("nv");user.setName("ls");int i = new Random().nextInt(100);System.out.println(i);user.setAge(i);userService.updateData(user);}

测试结果如下:

修改前:

在这里插入图片描述
修改后:

在这里插入图片描述

    好了,这下彻底完事了,终于可以下班了。A君 怀着快乐的心情,开始收拾包裹回家去咯


总结

    正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/64040.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【笔记】记录一个python初见的语法(一)

在看书里的代码时了解了一个python的语法知识&#xff0c;是有关class类的。 代码如下&#xff1a; class Vocab: #save"""文本词表"""def __init__(self, tokensNone, min_freq0, reserved_tokensNone):if tokens is None:tokens []if rese…

LabVIEW面向对象编程有什么特点?

LabVIEW面向对象编程&#xff08;OOP&#xff09;的特点主要体现在它如何结合传统面向对象编程&#xff08;OOP&#xff09;的理念与LabVIEW的图形化编程模式&#xff0c;提供灵活的抽象和模块化的功能。以下是LabVIEW面向对象编程的几个主要特点&#xff1a; ​ 1. 类&#x…

【LC】240. 搜索二维矩阵 II

题目描述&#xff1a; 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。每列的元素从上到下升序排列。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,4,7,11,15],[2,5,8,12,19],[3,6,…

一级考试真题(2019)

一级考试真题&#xff08;2019&#xff09;

AudioSegment 将音频分割为指定长度时间片段 - python 实现

DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” -------------------------------------------------------------…

【kubernetes】k8s集群的简述与搭建

简述 Kubernetes&#xff08;简称 K8s&#xff09;是一个开源的容器编排平台&#xff0c;用于自动化部署、扩展和管理容器化应用程序 关键特性 自动化部署和回滚&#xff1a;Kubernetes 可以自动化地部署和回滚应用程序&#xff0c;确保应用程序始终处于预期的状态。服务发现…

C++ 内存管理和模板与STL

此篇目是之后各种C库的基础 目录 内存管理 内存分布 内存管理方式 new和delete operator new 与 operator delete函数 实现原理 定位new表达式(placement-new) 模板基础 泛型编程 模板 函数模板 类模板 STL 组成部分 内存管理 内存分布 int globalVar 1; //全局变量 静…

在 Ubuntu 24.04.1 LTS (WSL) 中使用 openssl 生成 keybox.xml

看到“生成 keybox.xml”&#xff0c;大概率都会联想到 PIF 和 Tricky Store。这里就不多解释它们的用途了。最近在网上看到生成非 AOSP keybox 的教程&#xff0c;在这里做一些补充&#xff0c;并将代码打包成一个 Python 脚本。 参考自&#xff1a; Idea 提供者&#xff1a…

Redis篇-1--入门介绍

1、Redis概述 ‌Redis&#xff08;Remote Dictionary Server&#xff09;&#xff0c;全称为远程字典服务。‌是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value数据库。 Redis提供了多种数据类型的存储&#xff0c;来适应不同场景下的存储需…

antdv-<a-button>中属性的使用

UI组件库&#xff08;User Interface Component Library&#xff09;是一种预先构建好的、可重用的用户界面元素集合&#xff0c;旨在帮助开发者更快速、更简便地构建用户界面。这些组件通常包括按钮、表单、导航栏、模态框等&#xff0c;能够提供一致的外观和交互风格&#xf…

简单的多网卡选择指定网卡ip注册

简单的多网卡选择指定网卡ip注册 我们公司服务器上面有多个网卡&#xff0c;多网卡则本地ip有多个ip,我们启动服务的时候需要选定他特定的ip&#xff0c;我们服务需要特定的ip进行注册&#xff0c;才能进行正常的通讯功能&#xff0c;我们需要使用如下配置进行特定ip选择&…

鸿蒙NEXT开发案例:颜文字搜索器

【引言】 本文将介绍一个名为“颜文字搜索器”的开发案例&#xff0c;该应用是基于鸿蒙NEXT平台构建的&#xff0c;旨在帮助用户快速查找和使用各种风格的表情符号。通过本案例的学习&#xff0c;读者可以了解如何在鸿蒙平台上进行数据处理、UI设计以及交互逻辑的实现。 【环…

快速部署一套K8s集群-v1.28

快速部署一套K8s集群-v1.28 1.前置知识点 1.1 生产环境可部署Kubernetes集群的两种方式 目前生产部署Kubernetes集群主要有两种方式: kubeadmKubeadm是一个K8s部署工具,提供kubeadm init和kubeadm join,用于快速部署Kubernetes集群。 二进制包从github下载发行版的二进…

【硬件测试】基于FPGA的4ASK调制解调通信系统开发与硬件片内测试,包含信道模块,误码统计模块,可设置SNR

目录 1.算法仿真效果 2.算法涉及理论知识概要 3.Verilog核心程序 4.开发板使用说明和如何移植不同的开发板 5.完整算法代码文件获得 1.算法仿真效果 本文是之前写的文章: 《基于FPGA的4ASK调制解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR》 的…

用友U8+ API接口使用教程

前言 U8和其他的公开的开放API接口有一些差异&#xff0c;他是需要先对接的到代理服务器&#xff0c;通过代理服务器进行对接&#xff0c;所以只要保证U8能上网就能对接&#xff0c;和畅捷通T的模式有点类似 流程&#xff1a; 注册成为开发者&#xff08;用于创建用友U8 API应…

检查读取数据寄存器输出的多扇出

为使第二寄存器被 RAM 原语吸收&#xff0c;来自存储器阵列的数据输出位的扇出必须为 1 。这在下图中进行了说明。 检查地址 / 读取数据寄存器上的复位信号 不应复位存储器阵列。只有 RAM 的输出可以容许复位。复位必须是同步的&#xff0c;以便将输出寄存器推断到 RAM 基元…

MongoDB-ObjectID 生成器

前言 MongoDB中一个非常关键的概念就是 ObjectID&#xff0c;它是 MongoDB 中每个文档的默认唯一标识符。了解 ObjectID 的生成机制不仅有助于开发人员优化数据库性能&#xff0c;还能帮助更好地理解 MongoDB 的设计理念。 什么是 MongoDB ObjectID&#xff1f; 在 MongoDB …

渗透测试工具 -- SQLmap安装教程及使用

随着网络安全问题日益严峻&#xff0c;渗透测试成为了保护信息安全的重要手段。而在渗透测试的众多工具中&#xff0c;SQLmap凭借其强大的自动化SQL注入检测和利用能力&#xff0c;成为了网络安全专家必备的利器。那么&#xff0c;你知道如何高效地使用SQLmap进行漏洞扫描吗&am…

SpringBoot 整合 RabbitMQ 实现流量消峰

RabbitMQ 即一个消息队列&#xff0c;主要是用来实现应用程序的异步和解耦&#xff0c;同时也能起到消息缓冲&#xff0c;消息分发的作用。 消息中间件在互联网公司的使用中越来越多&#xff0c;刚才还看到新闻阿里将 RocketMQ 捐献给了 Apache&#xff0c;当然了今天的主角还…

Git-分支(branch)常用命令

分支 我们在做项目开发的时候&#xff0c;无论是软件项目还是其他机械工程项目&#xff0c;我们为了提高效率以及合理的节省时间等等原因&#xff0c;现在都不再是线性进行&#xff0c;而是将一个项目抽离出诸进行线&#xff0c;每一条线在git中我们就叫做分支&#xff0c;bran…