Spring深度分析IoC/DI机制 配置文件式 基本实现

Posted 在下右转,有何贵干

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring深度分析IoC/DI机制 配置文件式 基本实现相关的知识,希望对你有一定的参考价值。

技术图片


首先,本人来提供一个 容器接口

容器接口 —— ApplicationContext接口:

package edu.youzg.simulate_IoC.bean;

public interface ApplicationContext {
    public Object getBean(String beanId);
}

现在,本人来给出这个接口的实现类

容器接口实现类 —— ClassPathXmlApplicationContext类:

package edu.youzg.simulate_IoC.bean;

import edu.youzg.simulate_IoC.entity.BeanDefinition;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ClassPathXmlApplicationContext implements ApplicationContext {
    //以id为键,相应的BeanDefinition为值,封装从xml配置文件中所需读取到的的信息
    private static Map<String, BeanDefinition> xmlBeans = new HashMap<String, BeanDefinition>();
    //IOC容器,以id为键,对象为值,封装new出来的信息,以便我们进行“依赖注入”工作
    private static Map<String, Object> beanIoc = new HashMap<String, Object>();

    /**
     * 加载配置文件路劲,配置文件是一个xml文件
     *
     * @param path 配置文件的路径
     */
    public ClassPathXmlApplicationContext(String path) {
        try {
            //解析xml文件
            //SAXReader:可以类比是一个管道,用流的方式,把xml文件读出来
            SAXReader reader = new SAXReader();
            InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("beans.xml");
            //获取 整个文档对象
            Document document = reader.read(inputStream);
            //将 根节点 封装为 一个Element对象
            Element rootElement = document.getRootElement();
            //遍历子节点
            List<Element> beanList = rootElement.elements();

            //读取bean配置的时候,需要读取到 id、class、init、destory、autowire....等属性
            //将bean节点属性,以及 子节点“集合” 封装起来
            for (Element bean : beanList) {
                //封装bean
                BeanDefinition beanDefinition = new BeanDefinition();

                //取得bean元素的属性 属性被封装为 Attribute对象
                Attribute idatt = bean.attribute("id");
                Attribute classatt = bean.attribute("class");
                Attribute initatt = null;
                try {
                    initatt = bean.attribute("init");
                } catch (Exception e) {
                }
                Attribute scopeatt = null;
                try {
                    scopeatt = bean.attribute("scope");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                String idVal = idatt.getValue();
                String classVal = classatt.getValue();

                beanDefinition.setId(idVal);
                beanDefinition.setClasspath(classVal);
                String value = null;
                try {
                    value = initatt.getValue();
                } catch (Exception e) {
                }
                String scope = null;
                try {
                    scope = scopeatt.getValue();
                } catch (Exception e) {
                }
                beanDefinition.setInit(value);
                beanDefinition.setScope(scope);
                //储存起来
                xmlBeans.put(idVal, beanDefinition);
            }
            /*System.out.println("解析xmlbean完成,解析的结果为:");
            System.out.println("	" + JSON.toJSONString(xmlBeans, true));*/
            //对xmlBeans进行java的解析,解析为实际的xml中配置的java对象
            Set<String> keys = xmlBeans.keySet();
            for (String key : keys) {
                Object obj = newInstance(key);
                //将id和obj对应的储存储到IOC容器中,以便我们之后获取使用
                beanIoc.put(key, obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object getBean(String beanId) {
        //首先获取beanId对应的 scope配置信息
        BeanDefinition beanDefinition = xmlBeans.get(beanId);
        if (beanDefinition == null) {
            throw new RuntimeException("[Youzg Error]:
	索引[" + beanId + "]不存在!");
        }

        Object result = null;
        //获知 “单例性”,默认为 单例模式
        String scope = beanDefinition.getScope();
        if (null!=scope && scope.equals("propotype")) {
            result = newInstance(beanDefinition.getId());
            return result;
        } else if (scope.equals("singleton") || null==scope) {
            result = beanIoc.get(beanId);
            if (result == null) {
                throw new RuntimeException("[Youzg Error]:
	IoC容器中不存在[" + beanId + "]的对象");
            }
            return result;
        } else {
            throw new RuntimeException("[Youzg Error]:
	scope属性值[" + scope + "]无法识别!");
        }
    }

    //通过反射机制,创建Java对象
    private Object newInstance(String beanId) {
        Object obj = null;
        try {
            BeanDefinition beanDefinition = xmlBeans.get(beanId);
            //需要对xml解析后的java对象BeanDefinition 进行加工【将配置变为java对象】 还要存放在IOC容器中
            String classPath = beanDefinition.getClasspath();
            //加载这个类
            Class cls = Class.forName(classPath);
            //创建类的对象
            obj = cls.newInstance();
            //判断有没有配置生命周期
            if (beanDefinition.getInit() != null && !beanDefinition.getInit().equals("")) {
                //配置的初始化方法名称
                String initMethod = beanDefinition.getInit();
                //此刻调用obj的initMethod方法
                Method method = cls.getDeclaredMethod(initMethod);
                method.invoke(obj);//执行方法
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

}

接下来,本人再来给出一个 用于封装xml文件中 每一个bean节点信息BeanDefinition类

封装节点信息 —— BeanDefinition类:

package edu.youzg.simulate_IoC.entity;

/**
 * 用于封装xml文件中 每一个bean节点信息
 */
public class BeanDefinition {
    private String id;
    private String classpath;
    private String init;
    private String scope;

    public String getInit() {
        return init;
    }

    public void setInit(String init) {
        this.init = init;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getClasspath() {
        return classpath;
    }

    public void setClasspath(String classpath) {
        this.classpath = classpath;
    }

}

那么,通过上述的接口与类,我们就基本实现配置文件式的IoC/DI机制

测试:

现在,本人来提供一个测试类 以及 一个测试配置文件来测试下我们上面的代码是否复合要求:
首先是一个 要被自动注入的类:

package edu.youzg.simulate_IoC.test;

public class AccountService {

    public AccountService() {
        System.out.println("目标类的 构造方法");
    }

    public void sayHello() {
        System.out.println("hello xml IoC!");
    }

    public void doSomething() {
        System.out.println("目标类的 某方法");
    }

}

接下来是配置文件:

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

<beans id="nihao" class="">
    <!--将service交给 自定义的IOC容器-->
    <bean id="accountService" class="edu.youzg.simulate_IoC.test.AccountService" scope="singleton"></bean>
</beans>

那么,现在本人最后再来给出一个Demo类:

package edu.youzg.simulate_IoC.test;

import edu.youzg.simulate_IoC.bean.ApplicationContext;
import edu.youzg.simulate_IoC.bean.ClassPathXmlApplicationContext;

/**
 * @Author: Youzg
 * @CreateTime: 2020-04-26 00:35
 * @Description:带你深究Java的本质!
 */
public class Demo {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        AccountService service1 = (AccountService) context.getBean("accountService");
        AccountService service2 = (AccountService) context.getBean("accountService");
        service1.sayHello();
        System.out.println(service1 == service2);
    }

}

现在,本人来展示下运行结果:
技术图片

那么,可以看到,我们基本实现了IoC/DI机制!

接下来我们只需要处理一下子标签的注入就ok了


以上是关于Spring深度分析IoC/DI机制 配置文件式 基本实现的主要内容,如果未能解决你的问题,请参考以下文章

Spring+IOC(DI)+AOP概念及优缺点

Spring 框架的概述以及Spring中基于XML的IOC配置

Java框架之Spring01-IOC-bean配置-文件引入-注解装配

Spring:IOC/DI注解开发管理第三方bean

Spring掌握IOC/DI注解管理第三方bean

spring:IOC+DI