当前位置: 首页 > article >正文

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.kler.cn/a/390222.html

相关文章:

  • ubuntu20.04安装FLIR灰点相机BFS-PGE-16S2C-CS的ROS驱动
  • 基于非时空的离身与反身智能
  • 类别变量分析——卡方独立性检验卡方拟合优度检验
  • 并发基础:(淘宝笔试题)三个线程分别打印 A,B,C,要求这三个线程一起运行,打印 n 次,输出形如“ABCABCABC....”的字符串【举一反三】
  • 词嵌入方法(Word Embedding)
  • RoseTTAFold MSA_emb类解读
  • 【vue】封装一个可随时暂停启动无需担心副作用的定时器
  • AI - 人工智能;Open WebUI;Lobe Chat;Ollama
  • git clone相关问题和bug记录
  • 本地保存mysql凭据实现免密登录mysql
  • Ubuntu 18.04 安装Fast-planner
  • Ecmascript(ES)标准
  • 【新人系列】Python 入门(九):数据结构 - 中
  • 深入探讨Vue项目中缺少明显入口文件的原因及解决策略
  • Spring Boot框架:计算机课程与工程认证的桥梁
  • 【宝藏】浏览器端的模块化问题(1)
  • 浅谈Spring MVC
  • middleware中间件概述
  • Django博客网站上线前准备事项
  • 昇思大模型平台打卡体验活动:项目2基于MindSpore通过GPT实现情感分类
  • PHP和Python脚本的性能监测方案
  • 游戏中的设计模式及杂项
  • 【Android】名不符实的Window类
  • NVM 介绍及使用指南
  • 【C++学习笔记】第一个C++程序
  • 如何评估Elasticsearch查询性能的具体指标?