一、简化Bean的注册

如果每次注册一个Bean,都要像上节一样,手动写PropertyValues相关的代码,那太复杂了,我们希望读取XML文件,自动注册Bean,这样对于使用者,甚至不知道有BeanDefinition的存在

二、统一处理资源文件

新建资源接口,Spring对所有的资源文件,统一处理

  • 一个资源,最重要的就是拿到输入流,拿到输入流就可以读取文件
public interface Resource {InputStream getInputStream() throws IOException;
}

提供三个资源实现类,分别读取不同类型的文件,这就是策略模式

类路径下的文件(最常用)

public class ClassPathResource implements Resource{private final String path;private final ClassLoader classLoader;public ClassPathResource(String path) {this(path, null);}public ClassPathResource(String path, ClassLoader classLoader) {Assert.notNull(path, "Path must not be null");this.path = path;this.classLoader = classLoader != null ? classLoader : ClassUtil.getClassLoader();}@Overridepublic InputStream getInputStream() throws IOException {InputStream is = classLoader.getResourceAsStream(path);if (is == null) {throw new FileNotFoundException(path + " cannot be opened because it does not exist");}return is;}
}

文件系统下的文件

public class FileSystemResource implements Resource{private File file;public FileSystemResource(File file) {this.file = file;}@Overridepublic InputStream getInputStream() throws IOException {return Files.newInputStream(file.toPath());}
}

网络文件

public class UrlResource implements Resource{private final URL url;public UrlResource(URL url) {Assert.notNull(url,"URL must not be null");this.url = url;}@Overridepublic InputStream getInputStream() throws IOException {URLConnection con = url.openConnection();return con.getInputStream();}
}

资源加载器接口,简化资源类的使用,自动根据路径选择合适的加载类

  • 这又属于工厂方法设计模式
/*** @Author 孤风雪影* @Email gitee.com/efairy520* @Date 2025/1/2 22:16* @Version 1.0*/
public interface ResourceLoader {String CLASSPATH_URL_PREFIX = "classpath:";Resource getResource(String location);
}

资源加载器接口实现

  • 根据路径前缀,默认就是使用classpath策略
/*** @Author 孤风雪影* @Email gitee.com/efairy520* @Date 2025/1/2 22:18* @Version 1.0*/
public class DefaultResourceLoader implements ResourceLoader{@Overridepublic Resource getResource(String location) {Assert.notNull(location, "Location must not be null");if (location.startsWith(CLASSPATH_URL_PREFIX)) {//使用类路径加载器,去掉前缀return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()));}else {try {URL url = new URL(location);return new UrlResource(url);} catch (MalformedURLException e) {return new FileSystemResource(new File(location));}}}
}

三、从文件中读取Bean

定义BeanDefinitionReader接口,从文件中读取BeanDefinition,并且注册到Bean工厂,这里有三要素

  • 资源文件
  • Bean工厂
  • 读取BeanDefinition的逻辑(单个资源,多个资源,位置字符串)
/*** @Author 孤风雪影* @Email gitee.com/efairy520* @Date 2025/1/2 22:26* @Version 1.0*/
public interface BeanDefinitionReader {BeanDefinitionRegistry getRegistry();ResourceLoader getResourceLoader();void loadBeanDefinitions(Resource resource);void loadBeanDefinitions(Resource... resources);void loadBeanDefinitions(String location);
}

用抽象类AbstractBeanDefinitionReader实现接口,模板方法设计模式

  • Bean工厂和资源加载器都是确定的,抽象类直接实现
  • 只有加载BeanDefinition是不确定的逻辑,交给具体的策略子类实现
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {private final BeanDefinitionRegistry registry;private final ResourceLoader resourceLoader;public AbstractBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {this.registry = registry;this.resourceLoader = resourceLoader;}public AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {this(registry, new DefaultResourceLoader());}@Overridepublic BeanDefinitionRegistry getRegistry() {return registry;}@Overridepublic ResourceLoader getResourceLoader() {return resourceLoader;}}

XmlBeanDefinitionReader做具体实现,策略模式

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {super(registry, resourceLoader);}public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {super(registry);}@Overridepublic void loadBeanDefinitions(Resource resource) {try {InputStream is = resource.getInputStream();doLoadBeanDefinitions(is);} catch (IOException e) {throw new RuntimeException("IOException parsing XML document from " + resource, e);}}@Overridepublic void loadBeanDefinitions(Resource... resources) {for (Resource resource : resources) {loadBeanDefinitions(resource);}}@Overridepublic void loadBeanDefinitions(String location) {ResourceLoader resourceLoader = getResourceLoader();Resource resource = resourceLoader.getResource(location);loadBeanDefinitions(resource);}/*** 真正解析XMl文件的方法** @param inputStream*/private void doLoadBeanDefinitions(InputStream inputStream) {Document doc = XmlUtil.readXML(inputStream);Element root = doc.getDocumentElement();NodeList childNodes = root.getChildNodes();for (int i = 0; i < childNodes.getLength(); i++) {// 判断元素if (!(childNodes.item(i) instanceof Element)) continue;// 判断对象if (!"bean".equals(childNodes.item(i).getNodeName())) continue;// 解析标签Element bean = (Element) childNodes.item(i);String id = bean.getAttribute("id");String name = bean.getAttribute("name");String className = bean.getAttribute("class");// 获取 Class,方便获取类中的名称Class<?> clazz = null;try {clazz = Class.forName(className);} catch (ClassNotFoundException e) {throw new RuntimeException("不存在的类名" + className);}// 优先级 id > name,此处是Bean自己的id和nameString beanName = StrUtil.isNotEmpty(id) ? id : name;if (StrUtil.isEmpty(beanName)) {beanName = StrUtil.lowerFirst(clazz.getSimpleName());}// 定义BeanBeanDefinition beanDefinition = new BeanDefinition(clazz);// 读取属性并填充for (int j = 0; j < bean.getChildNodes().getLength(); j++) {if (!(bean.getChildNodes().item(j) instanceof Element)) continue;if (!"property".equals(bean.getChildNodes().item(j).getNodeName())) continue;// 解析标签:propertyElement property = (Element) bean.getChildNodes().item(j);String attrName = property.getAttribute("name");String attrValue = property.getAttribute("value");String attrRef = property.getAttribute("ref");// 获取属性值:引入对象、值对象Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;// 创建属性信息PropertyValue propertyValue = new PropertyValue(attrName, value);beanDefinition.getPropertyValues().addPropertyValue(propertyValue);}if (getRegistry().containsBeanDefinition(beanName)) {throw new RuntimeException("Duplicate beanName[" + beanName + "] is not allowed");}// 注册 BeanDefinitiongetRegistry().registerBeanDefinition(beanName, beanDefinition);}}
}

BeanDefinitionReader接口、资源接口,层次结构图

请添加图片描述

四、测试

新建Person类

@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Person {private String name;private int age;private Cat cat;
}

新建Cat类

@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Cat {private String name;private int weight;
}

编写一个spring.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans><bean id="cat" class="cn.shopifymall.springframework.test.bean.Cat"><property name="name" value="tomcat"/><property name="weight" value="2000"/></bean><bean id="person" class="cn.shopifymall.springframework.test.bean.Person"><property name="name" value="10001"/><property name="age" value="18"/><property name="cat" ref="cat"/></bean></beans>

新建测试类

public class ApiTest {@Testpublic void testGetBeanFromXml() {// 1.初始化 BeanFactoryDefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 2. 读取配置文件&注册BeanXmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);reader.loadBeanDefinitions("classpath:spring.xml");// 3. 获取Bean对象调用方法Person person = (Person) beanFactory.getBean("person");System.out.println("person:" + person);}
}

控制台输出

person:Person(name=10001, age=18, cat=Cat(name=tomcat, weight=2000))

五、总结

  • 通过引入spring.xml配置文件,我们就可以简化Bean的注册
  • 用户只需要编写一个xml文件,由XmlBeanDefinitionReader自动解析xml文件,生成BeanDefinition并注册到BeanFactory

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

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

相关文章

【数位DP】D. From 1 to Infinity

Problem - D - Codeforces 题目&#xff1a; 思路&#xff1a; 数位DP 数论 题目让我们求这个无限序列 123456789101112.... 的前 k 个数的数位和 题目看起来很不好求&#xff0c;事实上确实是这样的 我们可以先从简单问题开始 问题①. 求 k 位置对应着第几个数 那么显然…

gitlab、jenkins等应用集成ldap

gitlab、jenkins等应用集成ldap 文档 openldap安装 -添加条目gitlab、jenkins等应用集成ldap gitlab集成ldap gitlab版本&#xff1a;gitlab-jh-17.7.0 ldap版本&#xff1a;openldap-2.6.10 修改/etc/gitlab/gitlab.rb文件&#xff0c;编辑相关信息 gitlab_rails[ldap_en…

Unity中国小游戏行业沙龙:抖音小游戏平台分析与规划

目录 一、抖音小游戏市场全景分析 行业现状与发展趋势 行业发展关键议题 内容运营生态观察 二、平台技术架构与运营体系 用户复访与留存体系 技术支撑体系 三、平台激励与商业化政策 收益分成机制 资金服务升级 技术基础建设 四、生态合作与发展规划 开发者支持体系…

手机横屏适配方案

CSS自动旋转页面实战指南在移动端开发中&#xff0c;横屏适配是一个常见但棘手的问题。本文将深入解析一套完整的CSS横屏适配方案&#xff0c;让你的网页在手机旋转时自动调整布局&#xff0c;提供无缝的用户体验。一、横屏适配的重要性 随着移动设备使用场景的多样化&#xff…

蓝桥杯算法之基础知识(2)——Python赛道

1.循环里面套用递归&#xff0c;当递归执行return时&#xff0c;只会退出当前递归层2.不能一边遍历list 一边pop解决办法&#xff1a;倒序遍历解决或者创建新的列表去存储3.sqrt求出来的始终是小数形式&#xff0c;注意题目要求的结果有可能是整型你直接sqrt就提交&#xff0c;…

如何优雅解决 OpenCV 分段错误(Segfault):子进程隔离实战

在分布式数据平台&#xff08;如 Databricks Spark&#xff09;中跑视频处理任务时&#xff0c;你是否遇到过这种恶心的报错&#xff1f;Py4JJavaError: An error occurred while calling z:org.apache.spark.api.python.PythonRDD.collectAndServe. : org.apache.spark.Spark…

Docker的六种网络模式(详解)

文章目录1. bridge&#xff08;默认&#xff09;2. host3. none4. container5. overlay6. macvlan7. 总结对比Docker 六种网络模式是容器网络的基础概念&#xff0c;不同模式决定容器与宿主机、外部网络、其他容器之间的通信方式。 1. bridge&#xff08;默认&#xff09; Br…

微服务流量分发核心:Spring Cloud 负载均衡解析

目录 理解负载均衡 负载均衡的实现方式 服务端负载均衡 客户端负载均衡 Spring Cloud LoadBalancer快速上手 常见的负载均衡策略 自定义负载均衡策略 LoadBalancer 原理 理解负载均衡 在 Spring Cloud 微服务架构中&#xff0c;负载均衡&#xff08;Load Balance&#…

鸿蒙异步处理从入门到实战:Promise、async/await、并发池、超时重试全套攻略

摘要&#xff08;介绍目前的背景和现状&#xff09; 在鸿蒙&#xff08;HarmonyOS&#xff09;里&#xff0c;网络请求、文件操作、数据库访问这类 I/O 都是异步的。主流写法跟前端类似&#xff1a;Promise、async/await、回调。想把 app 做得“流畅且不阻塞”&#xff0c;核心…

【html2img/pdf 纯!纯!python将html保存为图片/pdf!!效果非常的棒!】

素材 a.png html card.html <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><title>固定样式卡片</title><style>/* 基础样式和页面居中 */body {font-family: "微软雅黑", "P…

带宽评估(三)lossbase_v2

一、优化方向 调整丢包恢复算法的参数:可以通过调整算法中的一些参数,如丢包恢复速率、丢包恢复阈值等,来优化算法的性能。 调整发送窗口大小:在固定丢包场景下,可以通过调整发送窗口大小来控制发送速率,从而减少丢包率。 a=fmtp:96 x-google-min-bitrate=300 二、Goo…

imx6ull-驱动开发篇29——Linux阻塞IO 实验

目录 实验程序编写 blockio.c blockioApp.c Makefile 文件 运行测试 在之前的文章里&#xff0c;Linux阻塞和非阻塞 IO&#xff08;上&#xff09;&#xff0c;我们学习了Linux应用程序了两种操作方式&#xff1a;阻塞和非阻塞 IO。 在Linux 中断实验中&#xff0c;Linux…

97. 小明逛公园,Floyd 算法,127. 骑士的攻击,A * 算法

97. 小明逛公园Floyd 算法dijkstra, bellman_ford 是求单个起点到单个终点的最短路径&#xff0c;dijkstra无法解决负权边的问题&#xff0c; bellman_ford解决了负权边的问题&#xff0c;但二者都是基于单起点和单终点。而Floyd 算法旨在解决多个起点到多个终点的最短路径问题…

​崩坏世界观中的安全漏洞与哲学映射:从渗透测试视角解构虚拟秩序的脆弱性​

​崩坏世界观&#xff1a;游戏中的世界&#xff0c;是真实&#xff0c;也是虚幻的&#xff01;对于游戏中的NPC角色而言&#xff0c;TA们生存的世界&#xff0c;是真实的&#xff01;对于游戏玩家而言&#xff0c;游戏中的世界&#xff0c;是虚拟的&#xff01;通过沉浸式的游戏…

【离线安装】CentOS Linux 7 上离线部署Oracle 19c(已成功安装2次)

1.部署参考链接&#xff1a; CentOS 7 rpm方式离线安装 Oracle 19chttps://blog.csdn.net/Vampire_1122/article/details/123038137?fromshareblogdetail&sharetypeblogdetail&sharerId123038137&sharereferPC&sharesourceweixin_45806267&sharefromfrom…

小白向:Obsidian(Markdown语法学习)快速入门完全指南:从零开始构建你的第二大脑(免费好用的笔记软件的知识管理系统)、黑曜石笔记

一、认识Obsidian&#xff1a;不只是笔记软件的知识管理系统 1.1 什么是Obsidian Obsidian是一个基于本地存储的知识管理系统&#xff0c;它将你的所有笔记以纯文本Markdown格式保存在电脑本地。这个名字来源于黑曜石——一种火山熔岩快速冷却形成的玻璃质岩石&#xff0c;象…

攻防世界—Confusion1—(模板注入ssti)

一.解题在login和register的页面中发现这个文件路径接下去就找有什么点可以利用二.ssti通过题目信息可知是一只蛇把一只大象缠绕起来了&#xff0c;蛇代表python&#xff0c;大象代表php这边通过python可以推测可能是模板注入&#xff0c;这边我看其他的解题是说通过看报文信息…

【Protues仿真】基于AT89C52单片机的超声波测距

目录 1 HCSR04超声波测距传感器 1.1 基本参数 1.2 引脚说明 1.3 工作原理&#xff08;时序图&#xff09; 2 基于AT89C52单片机的超声波测距电路原理图 2.1 硬件连接说明 2.2 工作原理 3 基于AT89C52单片机的超声波测距控制程序 3.1.1 初始化设置 3.1.2 超声波测距原…

LLM - Agent核心架构:四大“身体”部件

文章目录一、Agent核心架构&#xff1a;四大“身体”部件1. 核心大脑&#xff1a;大型语言模型&#xff08;LLM&#xff09;2. 记忆系统&#xff1a;短期与长期记忆3. 工具箱&#xff08;Toolkit&#xff09;&#xff1a;从“思想家”到“行动家”4. 驱动循环&#xff08;Engin…

html-docx-js 导出word

2025.08.23今天我学习了如何将html页面内容导出到word中&#xff0c;并保持原有格式&#xff0c;效果如下&#xff1a;代码如下&#xff1a;1&#xff1a;列表页面按钮<el-button type"warning" plain icon"el-icon-download" size"mini" cli…