Spring资源加载模块,原来XML就这,活该被注解踩在脚下 手写Spring第六篇了

这一篇让我想起来学习 Spring 的时,被 XML 支配的恐惧。明明是写Java,为啥要搞个XML呢?大佬们永远不知道,我认为最难的是 XML 头,但凡 Spring 用 JSON来做配置文件,Java 界都有可能再诞生一个扛把子。

<?xml version="1.0" encoding="UTF-8"?>

那今天我就要来盘一下,突破自己的心里障碍。把 Spring XML 的底裤都给扒掉,最后会发现,原来每个人的身上都有毛毛。

设计一下子

首先想想应该怎么设计这个模块?BeanDefinitionRegistry 这个伙计是大门的保安,把守着资源加载的大门。他的口头禅就是:穿内裤者 或 不打领带者不得入内。

image.png

作为一个专业的前端后端运维测试攻城狮,高内聚低耦合必须手到擒来,面向接口编程更是基本操作。BeanDefinitionReader 接口就是我们的协议,只要符合这个接口协议都能进入我们的大门。

image.png

这可是比武招亲严格多了,你再有本事,不按照规矩来,那也是白搭。

image.png

BeanDefinitionReader 接口一放出去,有两个年轻人,三十多岁,一个叫 XmlBeanDefinitionReader,一个叫 ResouceLoader,他们说要试试。这两个年轻人不知道天高地厚,以为我的类图只有这么点。实际上我只是按照传统功夫的点到为止截图而已。

image.png

不仅如此,我还有流程图。

image.png

上次我太将武德了,右眼睛被人蹭了一下。今天我 18 岁老码农是乱打的,类图,流程图,还有一个不知道什么图,训练有素。现在我就是这么不讲武德,来骗,来偷袭年轻人。你们年轻人自己耗子尾汁。

实现一下子

首先,我们来看看门面担当BeanDefinitionReader

public interface BeanDefinitionReader {  void loadBeanDefinitions(String location) throws IOException;  
}

这家伙就一个方法,简单得很!但是别小看它,这可是整个系统的总指挥!

然后是我们的主角 XmlBeanDefinitionReader

public class XmlBeanDefinitionReader implements BeanDefinitionReader {  private final BeanDefinitionRegistry beanDefinitionRegistry;  public XmlBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {  this.beanDefinitionRegistry = beanDefinitionRegistry;  }  @Override  public void loadBeanDefinitions(String location) throws IOException {  ResourceLoader resourceLoader = new DefaultResourceLoader();  loadBeanDefinitions(resourceLoader.getResource(location));  }  private void loadBeanDefinitions(Resource resource) throws IOException {  InputStream inputSteam = resource.getInputSteam();  doLoadBeanDefinitions(inputSteam);  }  private void loadBeanDefinitions(Resource... resources) throws IOException {  for (Resource resource : resources) {  loadBeanDefinitions(resource);  }  }  private void doLoadBeanDefinitions(InputStream inputStream) {  Document document = XmlUtil.readXML(inputStream);  Element root = document.getDocumentElement();  NodeList childNodes = root.getChildNodes();  for (int i = 0; i < childNodes.getLength(); i++) {  Node item = childNodes.item(i);  if (!(item instanceof Element)) continue;  if (!"bean".equals(item.getNodeName())) continue;  // bean 信息  Element element = (Element) item;  String id = element.getAttribute("id");  String name = element.getAttribute("name");  String className = element.getAttribute("class");  String beanName = StrUtil.isNotBlank(id) ? id : name;  Class<?> clazz;  try {  clazz = Class.forName(className);  } catch (ClassNotFoundException e) {  throw new BeanException(e.getMessage());  }  PropertyValues propertyValues = new PropertyValues();  BeanDefinition beanDefinition = new BeanDefinition(clazz, propertyValues);  // properties 信息  NodeList propertyNodes = element.getChildNodes();  for (int j = 0; j < propertyNodes.getLength(); j++) {  Node property = propertyNodes.item(j);  if (!(property instanceof Element)) continue;  if (!"property".equals(property.getNodeName())) continue;  Element propertyElement = (Element) property;  String propertyName = propertyElement.getAttribute("name");  String value = propertyElement.getAttribute("value");  String ref = propertyElement.getAttribute("ref");  PropertyValue propertyValue;  if (StrUtil.isNotBlank(ref)) {  BeanReference beanReference = new BeanReference(ref);  propertyValue = new PropertyValue(propertyName, beanReference);  } else {  propertyValue = new PropertyValue(propertyName, value);  }  propertyValues.addPropertyValues(propertyValue);  }  // 注册  beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);  }  }  
}

资源加载皮条哥,就看你选的哪个小弟给你干活。目前他能 hold 住资源内容读取三兄弟。

public interface ResourceLoader {  Resource getResource(String location);  
}public class DefaultResourceLoader implements ResourceLoader {  private final String CLASS_PATH_PREFIX = "classpath:";  @Override  public Resource getResource(String location) {  Objects.requireNonNull(location);  if (location.startsWith(CLASS_PATH_PREFIX)) {  String name = location.substring(CLASS_PATH_PREFIX.length());  return new ClassPathResource(name, getClassLoader());  }  try {  URL url = new URL(location);  return new UrlResource(url);  } catch (MalformedURLException e) {  return new FileSystemResource(location);  }  }  private ClassLoader getClassLoader() {  ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();  if (contextClassLoader != null) {  return contextClassLoader;  }  return ClassUtil.class.getClassLoader();  }  
}

资源内容读取三兄弟:

  1. ClassPathResource:专门找项目里的文件
  2. FileSystemResource:负责找电脑里的文件
  3. UrlResource:负责找网上的资源文件
public interface Resource {  InputStream getInputSteam() throws IOException;  
}
public class ClassPathResource implements Resource {  private final String name;  private final ClassLoader classLoader;  public ClassPathResource(String name, ClassLoader classLoader) {  Objects.requireNonNull(name);  this.name = name;  this.classLoader = classLoader;  }  @Override  public InputStream getInputSteam() throws IOException {  InputStream inputStream = classLoader.getResourceAsStream(name);  if (Objects.isNull(inputStream)){  throw new FileNotFoundException("Not found this file: "+ name);  }  return inputStream;  }  
}
public class FileSystemResource implements Resource {  private final String path;  private final File file;  public FileSystemResource(String path) {  this.path = path;  this.file = new File(path);  }  @Override  public InputStream getInputSteam() throws IOException {  return Files.newInputStream(file.toPath());  }  public String getPath() {  return path;  }  
}
public class UrlResource implements Resource {  private final URL url;  public UrlResource(URL url) {  this.url = url;  }  @Override  public InputStream getInputSteam() throws IOException {  URLConnection connection = url.openConnection();  try {  return connection.getInputStream();  } catch (IOException e) {  if (connection instanceof HttpURLConnection) {  ((HttpURLConnection) connection).disconnect();  }  throw e;  }  }  
}

测试一下子

首先准备一张菜单吗?告诉Spring:

  • 老板,来一个testDao
  • 再来一个testService,要加karl调料,顺便把刚才的testDao也放进去
<?xml version="1.0" encoding="UTF-8"?>  
<beans>  <bean id="testDao" class="pri.hongweihao.smallspring.bean.TestDao"/>  <bean id="testService" class="pri.hongweihao.smallspring.bean.TestService">  <property name="name" value="karl"/>  <property name="testDao" ref="testDao"/>  </bean>
</beans>
// 模拟dao对象
public class TestDao {  public void test() {  System.out.print("testDao");  }  
}// 模拟service对象
public class TestService {  private final String name;  private final TestDao testDao;  public TestService(String name, TestDao testDao) {  this.name = name;  this.testDao = testDao;  }  public void test() {  System.out.println("testService.name: " + this.name);  this.testDao.test();  }  
}

开干,我玩的就是真实

public class BeanFactoryTest {  @Test  public void test() throws IOException {  DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();  // 读取配置文件并自动注册  BeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);  beanDefinitionReader.loadBeanDefinitions("classpath:spring.xml");  // 从工厂中获取bean对象  TestService service = (TestService) defaultListableBeanFactory.getBean("testService", "", null);  service.test();  /*  打印结果        testService.name: karl     testDao        */    }  
}

搞定!是不是感觉XML也没那么可怕了?

总结

XML配置其实就是一张"菜单":

  1. Spring通过【资源】和【资源加载】帮我们找到这些配置文件
  2. 然后通过大厨 XmlBeanDefinitionReader 解析配置
  3. 最后把"菜"(Bean)放到"厨房"(容器)里

本文由 https://github.com/hongweihao/small-spring/tree/6_resource_load 赞注完成

本文完 | 求赞求关注求转发 !

image.png

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

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

相关文章

讲讲关于SNMP与智能PDU插座

什么是SNMP 简单网络管理协议 (SNMP) 是一种应用层协议&#xff0c;主要用于网络管理中的设备监控和控制。通过 SNMP&#xff0c;网络管理员可以从管理站远程访问网络中的设备&#xff0c;获取设备的状态信息、配置参数&#xff0c;甚至控制设备的行为。SNMP 被广泛应用于 TCP/…

丹摩征文活动 | Kolors入门:从安装到全面活用的对比指南

文章目录 1 图像生成模型 Kolors2 部署流程3 部署服务3.1 安装 Anaconda3.2 Kolors 库下载3.3 创建虚拟环境 4 生成图片 1 图像生成模型 Kolors Kolors是由快手团队开发的大规模文本到图像生成模型&#xff0c;以其独特的潜在扩散技术而闻名。 Kolors通过在数十亿对文本和图像…

【go从零单排】通道select、通道timeout、Non-Blocking Channel Operations非阻塞通道操作

&#x1f308;Don’t worry , just coding! 内耗与overthinking只会削弱你的精力&#xff0c;虚度你的光阴&#xff0c;每天迈出一小步&#xff0c;回头时发现已经走了很远。 &#x1f4d7;概念 select 语句是 Go 的一种控制结构&#xff0c;用于等待多个通道操作。它类似于 s…

信息安全工程师(83)Windows操作系统安全分析与防护

一、Windows操作系统安全分析 系统漏洞&#xff1a; Windows操作系统由于其复杂性和广泛使用&#xff0c;可能存在一些已知或未知的漏洞。这些漏洞可能会被黑客利用&#xff0c;进行恶意攻击。微软会定期发布系统更新和补丁&#xff0c;以修复这些漏洞&#xff0c;提高系统的安…

计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议

文章目录 一、TCP/IP五层模型&#xff08;重要&#xff09;二、应用层常见的协议三、TCP与UDP3.1 TCP、UDP的区别&#xff08;重要&#xff09;3.2 运行于TCP、UDP上的协议3.3 TCP的三次握手、四次挥手3.3.1 TCP的三次握手3.3.2 TCP的四次挥手3.3.3 随机生成序列号的原因 四、T…

BFD8122防爆轻便移动工作灯

BFD8122防爆轻便移动工作灯 适用范围&#xff1a; 适用于炼油、化工、油田等易燃易爆场所小范围施工、检修、抢险应急照明。 结构特性 高亮度&#xff0c;灯具光通量&#xff1e;4000lm&#xff0c;6米中心照度&#xff08;聚光&#xff09;&#xff1e;1000lx&#xff0c;…

天地图入门|标注|移动飞行|缩放,商用地图替换

“天地图”是国家测绘地理信息局建设的地理信息综合服务网站。集成了来自国家、省、市&#xff08;县&#xff09;各级测绘地理信息部门&#xff0c;以及相关政府部门、企事业单位 、社会团体、公众的地理信息公共服务资源&#xff0c;如果做的项目是政府部门、企事业单位尽量选…

分布式----Ceph部署(上)

目录 一、存储基础 1.1 单机存储设备 1.2 单机存储的问题 1.3 商业存储解决方案 1.4 分布式存储&#xff08;软件定义的存储 SDS&#xff09; 1.5 分布式存储的类型 二、Ceph 简介 三、Ceph 优势 四、Ceph 架构 五、Ceph 核心组件 #Pool中数据保存方式支持两种类型&…

linux详解,基本网络枚举

基本网络枚举 一、基本网络工具 ifconfig ifconfig是一个用于配置和显示网络接口信息的命令行工具。它可以显示网络接口的P地址、子网掩码、MC地址等信息&#xff0c;还可以用于启动、停止或配置网络接口。 ip ip也是用于查看和管理网络接口的命令。 它提供了比ifconfig更…

组件间通信(组件间传递数据)

组件间通信(组件间传递数据) 在 Vue.js 中&#xff0c;组件间通信是开发者需要经常处理的任务&#xff0c;特别是在构建具有多层次组件的复杂应用时。根据组件之间的关系和数据流的复杂程度&#xff0c;可以采用不同的通信方式。以下是常用的几种组件间通信方式&#xff1a; …

深度学习-图像评分实验(TensorFlow框架运用、读取处理图片、模型建构)

目录 0、实验准备 ①实验环境 ②需要下载的安装包 ③注意事项&#xff08;很关键&#xff0c;否则后面内容看不懂&#xff09; ④容易出现的问题 1、查看数据并读取数据。 2、PIL库里的Image包进行读取&#xff08;.resize更改图片尺寸&#xff0c;并将原始数据归一化处…

全球碳循环数据集(2000-2023)包括总初级生产力、生态系统净碳交换和生态系统呼吸变量

全球碳循环数据集&#xff08;2000-2023&#xff09; 数据介绍 PFTs_XGB FLUX 是一个基于 XGBOOST 机器学习模型的全球碳循环数据集。该数据集通过对全球植被功能类型&#xff08;PFTs&#xff09;的分类&#xff0c;结合了 FLUXNET、AmeriFlux 和 ICOS 通量站点的现场观测数据…

前端代码分析题(选择题、分析题)——this指向、原型链分析

this指向 普通函数&#xff1a;this 的指向由调用方式决定&#xff0c;可以是全局对象、调用该函数的对象&#xff0c;或者显式指定的对象。箭头函数&#xff1a;this 的指向在定义时确定&#xff0c;始终继承自外层函数作用域的 this&#xff0c;不会被调用方式影响。 var obj…

【SpringBoot】18 上传文件到数据库(Thymeleaf + MySQL)

Git仓库 https://gitee.com/Lin_DH/system 介绍 使用 Thymeleaf 写的页面&#xff0c;将&#xff08;txt、jpg、png&#xff09;格式文件上传到 MySQL 数据库中。 依赖 pom.xml <!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j --><depende…

手动搭建 Ghost 博客

操作场景 Ghost 是使用 Node.js 语言编写的开源博客平台&#xff0c;您可使用 Ghost 快速搭建博客&#xff0c;简化在线出版过程。本文档介绍如何在腾讯云云服务器&#xff08;CVM&#xff09;上手动搭建 Ghost 个人网站。 进行 Ghost 网站搭建&#xff0c;您需要熟悉 Linux …

类型转换指令及方法调用与返回指令

我的后端学习大纲 JVM学习大纲 4.类型转换指令&#xff1a; 类型转换指令说明 ①类型转换指令可以将两种不同的数值类型进行相互转换。 这些转换操作一般用于实现用户代码中的显式类型转换操作&#xff0c;或者用来处理字节码指令集中数据类型相关指令无法与数据类型一一对应的…

【LLM Agents体验 3】利用Open-WebUI+Ollama本地部署Qwen2.5:7B大模型的安装指南

Open WebUI是一种基于 Web 的用户界面&#xff0c;用于管理和操作各种本地和云端的人工智能模型。它提供了一个直观的图形化界面&#xff0c;使用户可以方便地加载、配置、运行和监控各种 AI 模型&#xff0c;而无需编写代码或使用命令行界面。 Open-WebUI 是一款功能强大且易于…

动态规划 —— dp 问题-买卖股票的最佳时机IV

前言 在开始之前先说一下本题与 买卖股票的最佳时机Ill 的解法很相似&#xff0c;也可以去参考lll 动态规划 —— dp 问题-买卖股票的最佳时机III-CSDN博客https://blog.csdn.net/hedhjd/article/details/143671809?spm1001.2014.3001.5501 1. 买卖股票的最佳时机IV 题目链接&…

软件测试学习记录 Day1

根据黑马程序员最新版的软件测试课程所做的笔记&#xff0c;需要原件后台私信&#xff1a; 练习提取测试点&#xff1a; 博主的答案&#xff0c;有不一样看法的可评论区讨论&#xff1a;

Kafka 快速入门(一)

1.1安装部署 1.1.1 集群规划 bigdata01bigdata02bigdata03zookeeperzookeeperzookeeperkafkakafkakafka 1.1.2 集群部署 官方下载地址&#xff1a;http://kafka.apache.org/downloads.html 检查三台虚拟机的zk是否启动&#xff1a;zkServer.sh start 默认启动方式 1)解压…