手写一个简单的Spring

为了更好的理解Spring框架底层原理,我们自己先来写一个简单spring。本文章为记录学习而用,如有错误欢迎改正

主要用到:
a. dom4j技术来解析xml文件(找到对应的bean标签和property标签)
b. 反射和类加载(创建对象)
c. 内省机制(给属性赋值)

  1. 导入jar包
    在这里插入图片描述
  2. 我们先准备几个实体类。实现getter、setter、toString等方法。
    在这里插入图片描述
    3.创建一个xml文件application.xml(名字随意),并在这个文件中把刚才创建的实体类以bean标签的方式进行存放。
    在这里插入图片描述
  3. 编写一个接口名为BeanFactory,这个接口是spring中的顶层接口,代表spring本身,定义了一些基础的方法,不提供给开发者直接使用。这里我先编写一个getBean方法用于通过名字获取对象。
    在这里插入图片描述
  4. 再编写一个接口名为ApplicationContext的方法并继承刚才创建的BeanFactory接口。这个接口定义一些额外的方法。
    在这里插入图片描述
    注意:实际Spring框架中ApplicationContext下面还有一个ConfigurableApplicationContext接口,包括一些扩展方法 refresh() 和 close() 。这里不做过多介绍。
  5. 编写接口ApplicationContext的实现类ClassPathXmlApplicationContext,代码如下(通过注解进行介绍)。
public class ClassPathXmlApplicationContext implements ApplicationContext{
    //6.在多线程的情况下,会有线程安全问题,所以这里我们使用ConcurrentHashMap(底层采用分段锁),并且只有一个,用来存放我们的bean对象,也就是我们的spring容器
    private static ConcurrentHashMap<String,Object> beans = new ConcurrentHashMap();

    //7.我们需要在创建这个对象的时候就将我们的bean放入到容器中。并且需要传入我们的资源文件名
    public ClassPathXmlApplicationContext(String resource){
        try {
            SAXReader saxReader = new SAXReader();
            InputStream in = ClassPathXmlApplicationContext.class.getClassLoader().getResourceAsStream(resource);
            if(in == null){
                throw new RuntimeException("没有找到该配置文件");
            }
            //8.我们需要通过dom4j读取xml文件并形成一个Document文件
            Document document = saxReader.read(in);
            //9.通过我们的ioc方法来实现创建对象
            ioc(document);
            //10在我们创建好对象之后对bean对象中的属性进行赋值
            di(document);
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("spring容器创建失败");
        }

    }
    private void di(Document document) {
        try {
            //获取我们bean的id
            //我们通过XPth获取文档下所有的bean标签
            List<Node> beanlist = document.selectNodes("//bean");
            //遍历所有的bean标签。获取到我们的id属性的值和class属性的值
            for (Node bean : beanlist) {
                //获取bean的class属性
                String className = bean.valueOf("@class");
                if (className == null || className.trim().equals("")) {
                    throw new RuntimeException("class属性不能为空");
                }
                //获取bean标签的id属性/name属性
                String name = bean.valueOf("@id");
                //判断beanId是否为空
                if (name == null || name.trim().equals("")) {
                    //继续判断是否有name属性
                    name = bean.valueOf("@name");
                    if (name == null || name.trim().equals("")) {
                        //id和name都没有,但是我们需要给bean娶一个名字。把class属性的值的最后一个单词送给他
                        name = className.substring(className.lastIndexOf(".") + 1);
                    }
                }

                //通过我们得到beanName去容器中查找bean
                Object obj = beans.get(name);
                //将我们的bean转换为Element方便使用XPth查找
                //List<Node> propertyList = bean.selectNodes("//property");
                Element element = (Element) bean;
                List<Element> elements = element.elements("property");
                for (Element node : elements) {
                    String pName = node.valueOf("@name");
                    //获取该属性的属性描述器
                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(pName, obj.getClass());
                    //判断该属性是否存在
                    if(propertyDescriptor == null){
                        throw new RuntimeException("该属性不存在");
                    }
                    //这里要判断是简单类型还是注入到spring容器中的bean类型
                    String refAttributeValue = node.valueOf("@ref");
                    if(refAttributeValue != null &&!refAttributeValue.trim().equals("")){
                        //查找是否有这个bean
                        Object target = beans.get(refAttributeValue);
                        if(target == null){
                            throw new RuntimeException("需要注入的bean不存在");
                        }
                        //通过BeanUtils将我们的名称和值映射给obj对象
                        BeanUtils.copyProperty(obj,pName,target);
                    }else {
                        String attributeValue = node.valueOf("@value");
                        if(attributeValue == null || attributeValue.trim().equals("")){
                            throw new RuntimeException("属性value不能为空");
                        }
                        BeanUtils.copyProperty(obj,pName,attributeValue);
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("DI失败");
        }
    }

    private void ioc(Document document) {
        try {
            //我们通过XPth获取文档下所有的bean标签
            List<Node> beanlist = document.selectNodes("//bean");
            //遍历所有的bean标签。获取到我们的id属性的值和class属性的值
            for (Node bean : beanlist) {
                //获取bean的class属性
                String className = bean.valueOf("@class");
                if(className == null || className.trim().equals("")){
                    throw new RuntimeException("class属性不能为空");
                }
                //获取bean标签的id属性/name属性
                String name = bean.valueOf("@id");
                //判断beanId是否为空
                if(name == null || name.trim().equals("")){
                    //继续判断是否有name属性
                    name = bean.valueOf("@name");
                    if(name == null || name.trim().equals("")){
                        //id和name都没有,但是我们需要给bean娶一个名字。把class属性的值的最后一个单词送给他
                        name = className.substring(className.lastIndexOf(".") + 1);
                    }
                }
                //拿到我们class属性的值,通过反射创建对象
                Object obj = Class.forName(className).newInstance();
                //将我们创建好的对象,以及获取到bean的id放入到我们创建好的容器当中
                beans.put(name,obj);
            }
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("IOC失败");
        }
    }
    @Override
    public Object getBean(String name) {
        //直接从我们的ConcurrentHashMap中查找
        Object bean = beans.get(name);
        if(bean == null){
            throw new RuntimeException("no such bean");
        }
        return bean;
    }
}

最后我们进行测试。

    @Test
    public void test(){
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("application.xml");
        CVN cvn = (CVN) classPathXmlApplicationContext.getBean("cvn");
        System.out.println(cvn);
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值