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

news2024/11/24 14:25:09

这一篇让我想起来学习 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.coloradmin.cn/o/2238796.html

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

相关文章

【C++】AVL树的了解和简单实现

目录 AVL树的概念 AVL树介绍 平衡因子 AVL树的插入 平衡因子的更新 【1】平衡因子为0 【2】平衡因子为1/-1 【3】平衡因子为2/-2 选择的处理 旋转的原则 右单旋 具体的三种情况&#xff1a; ​编辑 所有情况的概念图&#xff1a; 对于父亲指针的处理 &…

使用 PageHelper 在 Spring Boot 项目中实现分页查询

目录 前言1. 项目环境配置1.1 添加 PageHelper 依赖1.2 数据库和 MyBatis 配置 2. 统一的分页响应类3. 使用 PageHelper 实现分页查询3.1 Service 层分页查询实现3.2 PageHelper 分页注意事项 4. 控制层调用示例5. 常见问题与解决方案5.1 java.util.ArrayList cannot be cast t…

丹摩征文活动 | 丹摩智算:大数据治理的智慧引擎与实践探索

丹摩DAMODEL&#xff5c;让AI开发更简单&#xff01;算力租赁上丹摩&#xff01; 目录 一、引言 二、大数据治理的挑战与重要性 &#xff08;一&#xff09;数据质量问题 &#xff08;二&#xff09;数据安全威胁 &#xff08;三&#xff09;数据管理复杂性 三、丹摩智算…

彻底理解ARXML中的PDU

文章目录 一、DBC报文信号的发送二、ARXML报文信号的发送2.1 什么是PDU2.2 PDU的类型2.3 Container-I-PDU的发送 三、小结 在CANFD支持可变速率和更大的数据长度&#xff08;64字节&#xff09;的情况下&#xff0c;可以使用DBC和ARXML两种数据库格式来进行报文通信&#xff0c…

探索MoviePy:Python视频编辑的瑞士军刀

文章目录 &#x1f3ac; 探索MoviePy&#xff1a;Python视频编辑的瑞士军刀第一部分&#xff1a;背景介绍第二部分&#xff1a;MoviePy是什么&#xff1f;第三部分&#xff1a;如何安装MoviePy&#xff1f;第四部分&#xff1a;MoviePy的基本函数使用方法1. 视频剪辑2. 视频拼接…

前端请求后端php接口跨域 cors问题

只需要后端在网站的入口文件 一般都是 index.php 加上 这几行代码就可以了 具体的参数可以根据需要去修改 header("Access-Control-Allow-Origin: *"); header(Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS); header(Access-Control-Allow-Heade…

C++11的一些特性

1、列表初始化&#xff0c;对内置类型和自定义类型都可以使用列表进行初始化——一切都可以用列表初始化 不要和初始化列表混了 std::initializer_list临时对象作为函数的参数&#xff0c;用来接收{}括起来的的初始化列表 容器中有用initializer_list作为参数的构造函数&…

MySQL多系统安装配置教程(Windows、Ubuntu、Centos)

专题目标 • 掌握在Windows下安装MySQL数据库 • 掌握在CentOS下安装MySQL数据库 • 掌握在Ubuntu下安装MySQL数据库 一、在Windows下安装MySQL数据库 • Windows下推荐使用安装程序进行安装 • 安装程序下载地址&#xff1a;https://dev.mysql.com/downloads/ 通过上面的安装…

关于 npm 更新镜像源问题

npm&#xff08;Node Package Manager&#xff09;&#xff0c;是一个NodeJS包管理和分发工具&#xff0c;已经成为了非官方的发布Node模块&#xff08;包&#xff09;的标准。&#xff09; 查看当前npm版本 npm -v 10.9.0 执行以下命令报错 npm install --registryhttp…

Netty篇(入门编程)

目录 一、Hello World 1. 目标 2. 服务器端 3. 客户端 4. 流程梳理 &#x1f4a1; 提示 5. 运行结果截图 二、Netty执行流程 1. 流程分析 2. 代码案例 2.1. 引入依赖 2.2. 服务端 服务端 服务端处理器 2.3. 客户端 客户端 客户端处理器 2.4. 代码截图 一、Hel…

文本语义分块、RAG 系统的分块难题:小型语言模型如何找到最佳断点

文本语义分块、RAG 系统的分块难题&#xff1a;小型语言模型如何找到最佳断点&#xff1f; 转自jina最新的关于文本语义分块的分享和模型 之前我们聊过RAG 里文档分块 (Chunking) 的挑战&#xff0c;也介绍了 迟分 (Late Chunking) 的概念&#xff0c;它可以在向量化的时候减…

大数据技术在金融风控中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 大数据技术在金融风控中的应用 大数据技术在金融风控中的应用 大数据技术在金融风控中的应用 引言 大数据技术概述 定义与原理 发…

小程序中引入下载到本地的iconfont字体图标加载不出来问题解决

我这个是uniapp项目,字体图标都是一样的,在vue项目中web端、uniapp运行到h5都没问题,但是运行到小程序加载不出来,报错如下: 不让用本地路径,所以我们要转为base64编码,这里给大家提供一个工具,它可以把本地字体文件转为base64:transfonter 进入官网后,第一步: …

MYSQL隔离性原理——MVCC

表的隐藏字段 表的列包含用户自定义的列和由系统自动创建的隐藏字段。我们介绍3个隐藏字段&#xff0c;不理解也没有关系&#xff0c;理解后面的undo log就懂了&#xff1a; DB_TRX_ID &#xff1a;6 byte&#xff0c;最近修改( 修改/插入 )事务ID&#xff0c;记录创建这条记…

vue3 + element-plus 的 upload + axios + django 文件上传并保存

之前在网上搜了好多教程&#xff0c;一直没有找到合适自己的&#xff0c;要么只有前端部分没有后端&#xff0c;要么就是写的不是很明白。所以还得靠自己摸索出来后&#xff0c;来此记录一下整个过程。 其实就是不要用默认的 action&#xff0c;要手动实现上传方式 http-reque…

【C++课程学习】:二叉搜索树

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;C课程学习 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 二叉树搜索树的概念&#xff1a; 节点的结构&#xff1a; ⚽️结构&#xff1a; ⚽️ 构造函数&…

Axure是什么软件?全方位解读助力设计入门

在产品设计和开发领域&#xff0c;Axure是一款大名鼎鼎且功能强大的软件&#xff0c;它为专业人士和团队提供了卓越的设计支持&#xff0c;帮助他们将创意转化为实际可操作的产品原型。 一、Axure 的基本介绍 Axure是一款专业的原型设计工具&#xff0c;主要用于创建交互式的…

java里面使用groovy案例+详解

场景&#xff1a; 最近有一个计算商品运费的&#xff0c;如果商品的数量大于快递公司设置的数量 10 那么超出部分也需要计算额外运费&#xff0c;那么这些计算过程代码我能不能不在java里面写呢&#xff0c;用一种可配置化的方式来根据不同的传参计算出运费&#xff1f; 页面传…

单体架构 IM 系统之核心业务功能实现

在上一篇技术短文&#xff08;单体架构的 IM 系统设计&#xff09;中&#xff0c;我们讨论了在 “用户规模小、开发人员少、开发时间短” 的业务背景下&#xff0c;采取 “怎么简单怎么做&#xff0c;怎么快怎么来” 的研发策略&#xff0c;于是设计了 单体架构的IM系统&#x…

Linux部署nginx访问文件403

问题描述&#xff1a;在linux服务器上通过nginx部署&#xff0c;访问文件403 新配置了一个用户来部署服务&#xff0c;将部署文件更新到原有目录下&#xff0c;结果nginx访问403 原因&#xff1a;没有配置文件的读写权限&#xff0c;默认不可读写&#xff0c;nginx无法访问到文…