手把手写一个简单IOC(基于XML配置文件)

news2024/11/25 16:28:36

目录

创建模块

准备测试阶段测试用的Bean

编写ApplicationContext接口

编写ClassPathXmlApplicationContext

确定采用Map集合存储Bean

解析配置文件实例化所有Bean

解析配置文件实例化所有Bean

测试阶段1(实例化bean) 

Bean的属性赋值

测试阶段2(为bean对象进行赋值)


Spring IoC容器的实现原理:工厂模式 + 解析XML + 反射机制。

创建模块

采用Maven方式新建Module:myIocSpring

引入dom4j和jaxen的依赖,因为要使用它解析XML文件,还有junit依赖。

 <dependencies>
        <dependency>
            <groupId>org.dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>2.1.3</version>
        </dependency>
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.2.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

准备测试阶段测试用的Bean

这里我们准备2个bean,一个是学生类,一个是班级类,在本文中我们就书写简单的ref和基本数据类型的IOC(即使用value给简单属性赋值。使用ref给非简单属性赋值。)。放在com.study包下

public class Clazz {
    private  String name;
    private  Integer total;

    @Override
    public String toString() {
        return "Clazz{" +
                "name='" + name + '\'' +
                ", total=" + total +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getTotal() {
        return total;
    }

    public void setTotal(Integer total) {
        this.total = total;
    }
}

public class Student {
    private  String name;
    private  Integer age;
    private Clazz clazz;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", clazz=" + clazz +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Clazz getClazz() {
        return clazz;
    }

    public void setClazz(Clazz clazz) {
        this.clazz = clazz;
    }
}

准备测试阶段测试用的XML配置文件:IOC.xml文件放在类路径当中即可,我们这里把文件放到类的根路径下。

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

    <bean id="clazz" class="com.study.Clazz">
        <property name="name" value="21软件工程c"/>
        <property name="total" value="59"/>
    </bean>

    <bean id="student" class="com.study.Student">
        <property name="name" value="zzh"/>
        <property name="age" value="19"/>
        <property name="clazz" ref="clazz"/>
    </bean>

</beans>

这时的目录结构如下:

编写ApplicationContext接口

 ApplicationContext接口中提供一个getBean()方法,通过该方法可以获取Bean对象。

public interface ApplicationContext {
    /**
     * 根据bean的id获取bean实例。
     * @param beanId bean的id
     * @return bean实例
     */
    Object getBean(String beanId);
}

编写ClassPathXmlApplicationContext

ClassPathXmlApplicationContext是ApplicationContext接口的实现类。该类从类路径当中加载myspring.xml配置文件。

public class ClassPathXmlApplicationContext implements ApplicationContext{
    @Override
    public Object getBean(String beanId) {
        return null;
    }
}

采用Map集合存储Bean

采用Map集合存储Bean实例。Map集合的key存储beanId,value存储Bean实例。Map<String,Object>在ClassPathXmlApplicationContext类中添加Map<String,Object>属性。并且在ClassPathXmlApplicationContext类中添加构造方法,该构造方法的参数接收myspring.xml文件。同时实现getBean方法。

    /**
     * 存储bean的Map集合
     */
    private Map<String,Object> beanMap = new HashMap<>();

    /**
     * 在该构造方法中,解析myspring.xml文件,创建所有的Bean实例,并将Bean实例存放到Map集合中。
     * @param resource 配置文件路径(要求在类路径当中)
     */
    public ClassPathXmlApplicationContext(String resource) {

    }

    @Override
    public Object getBean(String beanId) {
        return beanMap.get(beanId);
    }

解析配置文件实例化所有Bean

在ClassPathXmlApplicationContext的构造方法中解析配置文件,获取所有bean的类名,通过反射机制调用无参数构造方法创建Bean。并且将Bean对象存放到Map集合中。

解析配置文件实例化所有Bean

public class ClassPathXmlApplicationContext implements ApplicationContext{
    /**
     * 存储bean的Map集合
     */
    private Map<String,Object> beanMap = new HashMap<>();

    /**
     * 在该构造方法中,解析myspring.xml文件,创建所有的Bean实例,并将Bean实例存放到Map集合中。
     * @param resource 配置文件路径(要求在类路径当中)
     */
    public ClassPathXmlApplicationContext(String resource) {
        SAXReader reader=new SAXReader();
        try {
            //读取配置文件
            Document document = reader.read(ClassLoader.getSystemClassLoader()
                    .getResourceAsStream(resource));
            //拿到所有的bean标签
            List<Node> beanNodes = document.selectNodes("//bean");
            //遍历集合拿到对bean进行处理
            beanNodes.forEach(beanNode->{
              Element beanElt=  (Element)beanNode;
              //获取id
                String id = beanElt.attributeValue("id");
                //获取className
                String className = beanElt.attributeValue("class");
                try {
                    //反射获取对象
                    Class<?> clazz = Class.forName(className);
                    Constructor<?> constructor = clazz.getDeclaredConstructor();
                    Object bean = constructor.newInstance();
                    //存储到Map集合中
                    beanMap.put(id,bean);
                }catch (Exception e){
                    e.printStackTrace();
                }
            });


        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object getBean(String beanId) {
        return beanMap.get(beanId);
    }
}

测试阶段1(实例化bean) 

public class test {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("IOC.XML");
        Object clazz = applicationContext.getBean("clazz");
        Object student = applicationContext.getBean("student");
        System.out.println(clazz);
        System.out.println(student);
    }
}

 通过测试Bean已经实例化成功了,属性的值是null,因为我们调用的都是无参构造方法,所以属性值都是默认值,接下来给bean赋值即可。

Bean的属性赋值

通过反射机制调用set方法,给Bean的属性赋值。

对每个bean标签下的所有property 标签,对标签内容进行解析,这里需要注意点的是判断是引用数据类型还是简单数据类型。

public class ClassPathXmlApplicationContext implements ApplicationContext{
    /**
     * 存储bean的Map集合
     */
    private Map<String,Object> beanMap = new HashMap<>();

    /**
     * 在该构造方法中,解析myspring.xml文件,创建所有的Bean实例,并将Bean实例存放到Map集合中。
     * @param resource 配置文件路径(要求在类路径当中)
     */
    public ClassPathXmlApplicationContext(String resource) {
        SAXReader reader=new SAXReader();
        try {
            //读取配置文件
            Document document = reader.read(ClassLoader.getSystemClassLoader()
                    .getResourceAsStream(resource));
            //拿到所有的bean标签
            List<Node> beanNodes = document.selectNodes("//bean");
            //遍历集合拿到对bean进行处理
            beanNodes.forEach(beanNode->{
              Element beanElt=  (Element)beanNode;
              //获取id
                String id = beanElt.attributeValue("id");
                //获取className
                String className = beanElt.attributeValue("class");
                try {
                    //反射获取对象
                    Class<?> clazz = Class.forName(className);
                    Constructor<?> constructor = clazz.getDeclaredConstructor();
                    Object bean = constructor.newInstance();
                    //存储到Map集合中
                    beanMap.put(id,bean);
                }catch (Exception e){
                    e.printStackTrace();
                }
            });
        beanNodes.forEach(beanNode->{
           Element beanElt=  (Element)beanNode;
            //获取bean的id
            String beanId = beanElt.attributeValue("id");
            //获取所有的property标签
            List<Element> propertyEls = beanElt.elements("property");
            propertyEls.forEach(propertyElt->{
                try {
                    //获取属性名
                    String propertyName = propertyElt.attributeValue("name");
                    //获取属性类型
                    Class<?> propertyType = beanMap.get(beanId).getClass().getDeclaredField(propertyName).getType();
                    //获取set方法名
                  String setMethodName=  "set"+propertyName.toUpperCase().charAt(0)+  propertyName.substring(1);
                    //获取set方法
                    Method setMethod = beanMap.get(beanId).getClass().getDeclaredMethod(setMethodName, propertyType);
                    //获取value的值
                    String propertyValue = propertyElt.attributeValue("value");
                    //获取ref
                    String propertyRef=propertyElt.attributeValue("ref");
                    Object propertyVal=null;
                    //如果不是简单属性
                    if(propertyValue!=null){
                        //获取属性类型名
                        String simpleName = propertyType.getSimpleName();
                        switch (simpleName) {
                            case "byte": case "Byte":
                                propertyVal = Byte.valueOf(propertyValue);
                                break;
                            case "short": case "Short":
                                propertyVal = Short.valueOf(propertyValue);
                                break;
                            case "int": case "Integer":
                                propertyVal = Integer.valueOf(propertyValue);
                                break;
                            case "long": case "Long":
                                propertyVal = Long.valueOf(propertyValue);
                                break;
                            case "float": case "Float":
                                propertyVal = Float.valueOf(propertyValue);
                                break;
                            case "double": case "Double":
                                propertyVal = Double.valueOf(propertyValue);
                                break;
                            case "boolean": case "Boolean":
                                propertyVal = Boolean.valueOf(propertyValue);
                                break;
                            case "char": case "Character":
                                propertyVal = propertyValue.charAt(0);
                                break;
                            case "String":
                                propertyVal = propertyValue;
                                break;
                        }
                        setMethod.invoke(beanMap.get(beanId),propertyVal);
                    }
                    //如果不是简单属性
                    if(propertyRef!=null){
                        setMethod.invoke(beanMap.get(beanId),beanMap.get(propertyRef));
                    }

                }catch ( Exception e){
                    e.printStackTrace();
                }
            });

        });

        } catch (DocumentException e) {
            e.printStackTrace();
        }

    }

    @Override
    public Object getBean(String beanId) {
        return beanMap.get(beanId);
    }
}

思考:为什么不在上面的循环中给Bean的属性赋值,而在这里再重新遍历一次呢? 

解答:很简单为了解决循环依赖问题,先生成bean对象进行曝光再赋值,实例化和属性赋值分开。

测试阶段2(为bean对象进行赋值)

public class test {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("IOC.XML");
        Object clazz = applicationContext.getBean("clazz");
        Object student = applicationContext.getBean("student");
        System.out.println(clazz);
        System.out.println(student);
    }
}

到这里一个简单的IOC已经实现,只实现的基本数据类型和ref类型。还是不能对LIst map等类型进行注入。 

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

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

相关文章

初阶数据结构——栈和队列习题

目录 括号匹配问题思路代码 用队列实现栈思路注意点代码 用栈实现队列思路代码 设计循环队列思路数组实现代码链表实现代码 括号匹配问题 OJ链接 思路 是左括号则入栈&#xff0c;是右括号则出栈然后两两比较 代码 #include<stdio.h> #include<stdlib.h> #i…

洛谷P1157详解(两种解法,一看就会)

一、问题引出 组合的输出 题目描述 排列与组合是常用的数学方法&#xff0c;其中组合就是从 n n n 个元素中抽出 r r r 个元素&#xff08;不分顺序且 r ≤ n r \le n r≤n&#xff09;&#xff0c;我们可以简单地将 n n n 个元素理解为自然数 1 , 2 , … , n 1,2,\dot…

chatgpt赋能Python-pycharm桌面图标变白

PyCharm是一个非常流行的Python集成开发环境&#xff0c;它可以帮助Python开发人员更快更高效地编写代码。但是&#xff0c;有些用户可能会遇到一个问题&#xff1a;他们的PyCharm桌面图标突然变成了白色&#xff0c;而不是原来的彩色图标。在这篇文章中&#xff0c;我将详细介…

面试官:为什么有了sleep还需要wait?

1. 能不能调整线程先后顺序&#xff1f; 对于线程执行最大的问题就是随机调度&#xff0c;抢占式执行&#xff0c;对于程序猿来讲&#xff0c;是不喜欢这种随机性的&#xff0c;程序猿喜欢确定的东西&#xff0c;于是就有了一些方法&#xff0c;可以控制线程之间的执行顺序&…

Keil 手动添加自己的lib库

我是在做一个项目看蓝牙模块官方Demo程序时发现对库的使用&#xff0c; 库大家都知道是不需要编译的&#xff0c;而且别人是无法看到源代码的&#xff0c;这样的好处就是编译快&#xff0c;并且方便移植&#xff0c;更安全。 我的制作步骤如下&#xff1a; 1、首先要把整个工程…

大量电脑桌面文件一键批量复制移动

电脑桌面上的文件越来越多&#xff0c;处理起来越来越繁琐。如果需要把这些文件复制或移动到另一个文件夹中&#xff0c;手动一个个复制或移动不仅费时费力&#xff0c;还容易出错。那么有没有什么方法可以快速批量复制和移动这些文件呢&#xff1f;当然有&#xff01;今天小编…

若依前后端分离如何动态渲染echarts图表

本章教程,主要介绍一下如何在若依前后端分离框架中,进行echarts动态渲染数据。 ECharts是一款基于JavaScript的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。ECharts最初由百度团队开源,并于2018年初捐赠给Apache基金会,成为ASF孵化级项目。…

027python-ddt

在unittest中&#xff0c;测试用例是不能传self以外的参数的&#xff0c;否则会报错import unittestclass TeatMath(unittest.TestCase):# def test_add(self,q): # TypeError: TeatMath.test_add() missing 1 required positional argument: q——>测试用例传参报错def t…

【KD-Tree】基于k-d树的KNN算法实现

文章目录 一、什么是KD-Tree&#xff1f;二、k-d树的结构三、k-d树的创建四、k-d树的应用五、KD-Tree的优缺点 例题JZPFAR 一、什么是KD-Tree&#xff1f; KD-Tree&#xff0c;又称&#xff08;k-dimensional tree&#xff09;&#xff0c;是一种基于二叉树的数据结构。它可以…

大项目推进

拉取最新的小组分支&#xff0c;创建自己开发分支&#xff08;对应实现人&#xff09; 任务分支&#xff08;小组名-功能名-执行人&#xff0c;如&#xff1a;f1-login-zhangfei&#xff09; 根据业务流程整理文档梳理与书写 定义领域模型&#xff08;包括&#xff1a;query、…

通用医学人工智能基础模型(GMAI)

最近&#xff0c;Eric J. Topol和 Pranav Rajpurkar研究团队提出了一个通用医学人工智能基础模型&#xff0c;文章名字《Foundation models for generalist medical artificial intelligence》 模型研究进展包括&#xff1a;多模态架构&#xff0c;和自监督学习技术&#xff0…

知识库AI机器人客服接口对接-唯一客服系统文档中心

如果你的需求仅仅是对接自训练的ChatGPT接口&#xff0c;实现自己的个性化机器人&#xff0c;那么可以看看下面的个性化ChatGPT调用接口前提条件是已经搭建好了知识库服务&#xff0c;该服务默认监听端口8083 chat接口地址 POST http://127.0.0.1:8083/data_collection/searchS…

“伙伴+华为”体系,数字时代的新航标

如果从1994年中国实行税制改革&#xff0c;要求以“以计算机网络为依托”开展企业税务工作算起&#xff0c;转瞬间&#xff0c;中国企业的信息化、数字化建设已经走过了近三十年历程。 这期间&#xff0c;信息化、数字化成为了企业走向管理现代化、全球化的依托&#xff0c;成为…

最新版本的Android studio 集成高德地图的定位功能

android studio版本&#xff1a; 1、根据高德官网链接集成 2、配置key的时候有两个注意点&#xff1a; a .获取安全SHA1 根据高德推荐的方式获取时&#xff0c;可能C:\Program Files\Android\Android Studio\jre\bin目录下找不到keytool.exe; 可以根据以下方式获取&#xff1…

华为新模拟器eNSPLite下载,部署教程及产品使用文档

华为新模拟器eNSPLite下载&#xff0c;部署教程及产品使用文档 如需下载请到我的博客中下载 硬件要求 数通培训认证模拟器支持在个人PC和物理服务器上部署安装&#xff0c;如下所示。 硬件推荐配置CPUX86_64架构CPU&#xff0c;支持VT-x/AMD-V 8核或以上RAM16G或以上DISK40G以…

数据库信息速递 支持机器学习的10个数据库 (译)

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

Spring:自动装配 Bean 的两种方式、使用注解开发

文章目录 Spring&#xff1a;Day 02一、自动装配 Bean1. 搭建环境2. 自动装配方式一&#xff1a;xml 配置3. 自动装配方式二&#xff1a;注解 二、注解开发三、使用 JavaConfig 实现配置 Spring Spring&#xff1a;Day 02 一、自动装配 Bean 在 Spring 中有三种装配的方式&am…

PostgreSQL表用户列最大个数

PostgreSQL表用户列最大个数 有些业务可能有这么个需求&#xff1a;需要增加用户列&#xff0c;即通过ALTER TABLE ... ADD...来添加用户列。那么PG/GP中是否会有列个数的限制呢&#xff1f; 它有1600列数的限制&#xff0c;并且没有方法去除掉这个限制。参见&#xff1a; http…

千年平阴玫瑰,绽放数字新魅力

“人间美景五月天、玫瑰花放霞流丹。” 每年的五月&#xff0c;济南市平阴县总是一幅玫瑰花芳香如海的迷人景象。作为中国玫瑰之乡&#xff0c;平阴地处古东原之阴&#xff0c;位于北纬36度“玫瑰种植黄金带”之上&#xff0c;这里土肥地沃、气候温和&#xff0c;属暖温带大陆…

Netty实战(四)

本节我们看看Netty的传输&#xff08;全是干货&#xff0c;自带水杯&#xff09; 一、Java的NIO和OIO1.1 OIO1.2 NIO 二、Netty的NIO和OIO2.1 OIO2.2 NIO 三、传输API四、内置的传输4.1 NIO4.2 Epoll—用于 Linux 的本地非阻塞传输4.3 OIO4.4 用于 JVM 内部通信的 Local 传输4.…