SpringIOC的Bean对象实例化的模拟实现

目录

1、我的上一篇文章

2、本篇博客的目的和内容

3、简单的思想和需要的POM依赖

4、项目的文件结构

 5、编写XML文件

6、编写XML文件中bean标签对应的类MyBean

7、 拥有getBean方法的接口:MyFactory

8、MyFactory接口的实现类MyClassPathXmlApplicationContext

9、Main方法中进行测试

10、我的下一篇博客


1、我的上一篇文章

在我的上一篇文章中,我简单的看了Spring官网中对于SpringFramework的介绍,初步使用了SpringFramework,将配置搭了起来,程序可以正常运行,也初步的解释了一些文件的作用和文件以及类之间的关系,下面是我的上一篇文章的地址:

SpringFramework简单的环境搭建_你是我的日月星河的博客-CSDN博客_springframework 环境搭建

2、本篇博客的目的和内容

本篇博客我将会手动模拟实现SpringFramework的SpringIOC的工作原理,也就是实体类Bean对象的实例化。会有代码和代码的讲解

3、简单的思想和需要的POM依赖

先说一下大概的模拟方法:我们就完全按照SpringFramework的思路,对它里面的每一个类或者是接口,文件都尽量的去模拟,按照它的思路去自己写代码(毕竟是初学,就不要另辟蹊径了)。

在SpringFramework中,每个bean标签都有一个id属性和class属性,当我们通过ApplicationContext对象的getBean方法,以XML配置文件中bean标签的id属性值作为参数的时候,可以直接就得到了一个实例化好的Bean对象(不需要我们去new了),这很明显,一个id属性值就对应一个实例化好的对象,这明显是Map的结构啊!

以上只是一种想法。这里先说一下,我们手动模拟的过程中,也需要使用到XML文件,只不过不需要我们写XML文件的那些文件头,因为文件头主要规定了能够使用那些标签,标签中有那些属性,以及标签之间的嵌套关系。我们实际编程手动首先的时候,按照万物皆对象的原则,我们可以先创建一个Bean类,这个Bean类专门存储XML文件中bean(这里的bean只是一个名称而已,我们自己写XML文件,可以随便定义,你定义为s标签也行)标签里面的id(这个属性是跟上面的bean标签同理的,名称我们可以任意写,因为是我们自己模拟SpringIOC过程,在写XML文件嘛)属性和class属性的值。

现在还有一个问题,我们写好的XML文件,怎么去读取这个文件呢?

这里我们可以使用现有的jar包,也就是导入如下的两个依赖:

上面就是我们需要使用的POM依赖,可以帮助我们读取XML文件里面的内容。

4、项目的文件结构

 5、编写XML文件

下面是XML文件的内容:

 由于是我们自己写的XML文件,因此就是我们随便定义的beans和bean标签,里面还有id属性和class属性,再说一下:这些标签名和属性名是可以自己随意定义的,我这样定义只是为了容易理解。

UserDao和UserService都是两个实体类,这两个实体类的代码我就不写了,里面各自简单的写一个公有的公共方法就行啦

6、编写XML文件中bean标签对应的类MyBean

bean标签中,我们定义了id属性和class属性,我们是需要读取XML文件的,将标签中的属性值读取出来,这里我们使用一个实体类来存储,MyBean类如下所示:

7、 拥有getBean方法的接口:MyFactory

我们手动模拟的MyFactory接口就类似于SpringFramework中的ApplicationContext接口或者是BeanFactory接口。代码如下所示:

package com.example.factory;

/*
这个是我们的自定义工厂类
相当于  Spring  的  ApplicationContext接口或者说是它的父接口  BeanFactory

 */
public interface MyFactory {

    //通过我们的XML文件中的  id属性值获取我们的实例化对象
    public Object getBean(String id);
}

8、MyFactory接口的实现类MyClassPathXmlApplicationContext

MyClassPathXmlApplicationContext类就类似于SpringFramework中的ClassPathXmlApplicationContext类或者是FileSystemXmlApplicationContext类,就是需要真正读取XML文件的类。当然,这个类是需要继承MyFactory接口的。代码如下:

package com.example.factory;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
import org.dom4j.xpath.DefaultXPath;

import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//这个是我们对应工厂的接口实现类
//相当于Spring的  ClassPathXmlApplicationContext 类
/*
    1、我们要读取我们的配置文件,也就是将我们的配置文件读取出来;
      我们通过这个类的构造函数的方法传过来
    2、然后我们要解析这个配置文件,得到每一个bean标签的id属性值和class属性值,并设置到对应的MyBean对象中,再存到集合中
    3、然后我们要遍历这个集合,得到每一个class属性对应的实例化对象,然后设置到Map中,id值多为key,这个实例化对象作为value
    4、我们会通过getBean方法,从Map对象中通过id获取指定的value,value也就是实例化对象

 */
public class MyClassPathXmlApplicationContext implements MyFactory{

    //我们首先定义一个 Map对象  用来存放id属性和对应实例化好的 Bean对象
    private Map<String,Object> beanMap=new HashMap<>();
    //定义List集合,用来存放配置文件中bean标签对应的id与class属性值得 MyBean对象
    private List<MyBean> beanList=null;


    //1、我们要读取我们的配置文件,也就是将我们的配置文件读取出来;
    //      我们通过这个类的构造函数的方法传过来
    public MyClassPathXmlApplicationContext(String fileName){
        parseXml(fileName);

        instanceBean();
    }

    //3、然后我们要遍历这个集合,得到每一个class属性对应的实例化对象,然后设置到Map中,id值多为key,这个实例化对象作为value
    private void instanceBean() {
        //我们首先判断我们的  beanList是否为空?
        if (beanList!=null&&beanList.size()>0){
            //这里我们遍历beanList集合,得到对应的  MyBean对象
            for (MyBean myBean : beanList) {
                //得到id值
                String id = myBean.getId();
                //得到class值
                String clazz = myBean.getClazz();
                //接下来我们要通过反射,实例化指定class属性值对应的Bean对象
                try {
                    //这里多说一下,下面的这条语句更好,是目前推荐的
                    //第二行代码从 JAVA 9开始已经不推荐了,使用的时候你会看到花了一条线
                    Object object = Class.forName(clazz).getDeclaredConstructor().newInstance();
                   //  Object object = Class.forName(clazz).newInstance();
                    //将我们的id值与实例化好的bean对象,设置到map中
                    beanMap.put(id,object);
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }

        }

    }

    //  2、然后我们要解析这个配置文件,得到每一个bean标签的id属性值和class属性值,并设置到对应的MyBean对象中,再存到集合中
    private void parseXml(String fileName) {
        try {
        //首先得到我们的解析器  是在我们的  dom4j  jar包里面的
        SAXReader reader=new SAXReader();
        //下面我们还要得到配置文件的  URL   这个代码我还不是很理解, 需要查一下
        URL url = this.getClass().getClassLoader().getResource(fileName);
        //下面我们要解析配置文件,得到一个 Document对象
            Document document = reader.read(url);
            //这个文档对象我们就需要去解析,  这里我们会使用到  XPath语法
            //这里我们可以到菜鸟教程上看一看关于  XPath的介绍,简单的学习一下
            //我们肯定要获取 beans标签下的所有的  bean标签
            //XPath使用路径表达式来选取 XML文档中的 节点或者是节点集   这个XPath是一个接口
            XPath xPath = document.createXPath("beans/bean");
            //上面我们是需要这个  文档创建一个 有特殊功能的XPath对象以后,由这个对象按照它的特殊功能操作这个文档

            //下面我们通过XPath语法得到对应的bean标签, 会返回一个  Element集合  Element也是dom4j里面的对象
            List<Element> elementList=xPath.selectNodes(document);
            //我们要先判断一下这个集合是否为  空
            if (elementList!=null&&elementList.size()>0){
                beanList=new ArrayList<>();//因为我们上面没有实例化
                //这里我们遍历Element集合,得到Element对象,得到标签对应的属性值
                for (Element element : elementList) {
                    //首先得到id属性的值  attributeValue:通过指定属性名得到对应的属性值
                    String id=element.attributeValue("id");
                    String clazz=element.attributeValue("class");
                    //下面将我们的 id属性值和class属性值设置到我们的一个 MyBean对象中
                    MyBean myBean=new MyBean(id,clazz);
                    beanList.add(myBean);//将这个MyBean对象添加到集合里面
                }
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }

    }

    // 4、我们会通过getBean方法,从Map对象中通过id获取指定的value,value也就是实例化对象

    @Override
    public Object getBean(String id) {
        //这个就是通过id,从我们的Map对象中获取对应的实例化对象
        Object object = beanMap.get(id);
        return object;
    }
}

这个类的代码比较的长,我里面也有注释,下面我还是解释一下代码:

首先就是一个beanMap属性,这个属性就是存放XML文件中bean标签的id属性值和它对应的实体类的(这里你也许就发现了:我们使用SpringIOC的依赖注入,每一次得到的其实是堆里面的同一个实体类。这一点也说明,某一些实体类是不能交给SpringIOC管理的,比如Student类,不可能每一个Student都是一样的吧!!)

然后是一个MyBean类型的List集合,我们读取XML文件的时候,每一个bean标签都会对应一个MyBean对象,这时候我们就将这些MyBean对象存储在List集合中,使用起来也方便。

再往下,就是我们的一个有参构造方法,这个参数是一个字符串,字符串就是XML文件的文件名,或者说是它的路径,然后下面调用内部的parseXml方法和instanceBean方法,读取XML文件的内容,并且将数据整理出来,存到beanMap属性和MyBean属性中。

parseXML()方法:这个方法就是读取XML配置文件的,将每一个XML文件里面的bean标签都对应生成一个MyBean实体类对象,然后放入List集合中。这个方法中的代码我目前也还不是很理解,只能看我上面的注释了。

instanceBean()方法:我们再parseXML()方法中,已经将XML文件中的id属性值和class属性值读取了出来,组成一个MyBean对象,放在了List集合中。class属性值就是对应实体类的地址,我们需要利用JAVA的反射机制,根据这个地址,实例化出对应的对象。然后将id属性值和实例化出来的这个对象放入Map中,id属性值作为键,实例化出来的对象作为值。对于JAVA的反射机制,具体的实现原理,我现在也不是很理解,后面我懂了,再补充博客。其实反射简单来看就是根据提供的类路径字符串,new出来对象。

getBean()方法:这个方法是我们继承了接口以后,需要实现的方法。这个方法就比较简单了,方法的参数就是一个字符串,根据这个字符串,到Map中寻找对应的已经实例化好的对象,然后返回就可以了。

9、Main方法中进行测试

编写Main方法,测试一下,代码如下:

package com.example;

import com.example.factory.MyClassPathXmlApplicationContext;
import com.example.factory.MyFactory;
import com.example.service.UserService;

public class Starter {
    public static void main(String[] args) {
        //先得到我们的一个工厂类
        MyFactory myFactory=new MyClassPathXmlApplicationContext("spring.xml");
        //然后通过myFactory的方法,得到指定的Bean对象
        UserService userService = (UserService) myFactory.getBean("userService");
        userService.test();
    }
}

控制台是可以正确执行test()方法的,并不出现空指针异常的情况。

10、我的下一篇博客

SpringIOC如何加载配置文件_你是我的日月星河的博客-CSDN博客https://blog.csdn.net/weixin_46281472/article/details/124183794

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你是我的日月星河

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值