Day1 手写DI (Dependency Injection, 依赖注入)

DI

什么是DI

DI: Dependency Injection, 依赖注入

  1. DI和IOC的关系
    IOC是一种设计思想,用于降低程序见的耦合度。DI是IOC的一种实现,IOC的另一常见的实现方式是DL(Dependency Lookup,依赖查找)。
    Spring使用的是DI。

  2. 依赖
    主要类之间的依赖, 例如下面代码,我们可以认为UserDao是UserServiceImpl的一个依赖:

    public class UserServiceImpl implements UserService {
     private UserDao userDao;
  3. 注入
    不需要从容器获取,Spring已经帮我们处理好所有依赖对象了。

例如,订购报纸或者牛奶,我们只需要告诉订购商需要什么,然后每天早上家门口就会放好报纸或者牛奶。
与“注入”相对应的是“查找”,每天早上需要自己去附近的订购点取报纸或者牛奶。

手写DI

Spring的核心是IOC容器,请使用DI技术自己实现一套IOC容器框架,以加深对Java和Spring的理解。

技术点:

  1. Java反射
  2. XML配置文件解析

实现方式:

  1. 解析XML文件中所有bean,以及bean属性;
  2. 利用反射技术,给解析的bean创建对象,并设置相关属性;
  3. 将创建的对象存放到map中,这个map就是一个“IOC容器”;
  4. 容器对象通过getBean(beanId)的方式获取,该方法实际从map中获取对象;

构造函数注入

通过构造函数来进行依赖注入

修改XML文件

xml配置示例:

<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userDao" class="com.bailiban.day1.dao.impl.UserDaoImpl" />
    <bean id="userService" class="com.bailiban.day1.service.impl.UserServiceImpl">
        <constructor-arg name="userDao" ref="userDao" />
    </bean>
    <bean id="client" class="com.bailiban.day1.Client">
        <constructor-arg name="userService" ref="userService" />
    </bean>
</beans>

注意:
配置文件中<constructor-arg name="userDao" ref="userDao" />表示构造函数的参数名称和取值。

扫描二维码关注公众号,回复: 8217457 查看本文章
IOC容器编码

示例代码:

public class BeanFactory {

    private static Map<String, Object> beanMap = new HashMap<>();

    public static void refresh(String xmlPath) throws Exception {
        // 创建SAXReader
        SAXReader saxReader = new SAXReader();
        // 获取xml配置文件
        URL xmlUrl = BeanFactory.class.getClassLoader().getResource(xmlPath);
        // 将SAXReader和xml配置文件绑定
        Document doc = saxReader.read(xmlUrl);
        // 获取根元素“beans”
        Element root = doc.getRootElement();
        // 遍历bean节点
        List<Element> beanElementList = root.elements();
        for (Element beanElement: beanElementList) {
            parseBeanElement(beanElement);
        }
    }

    private static void parseBeanElement(Element beanElement) throws Exception {
        String id = beanElement.attributeValue("id");
        String clsName = beanElement.attributeValue("class");
        // 获取Class对象
        Class<?> cls = Class.forName(clsName);
        // 获取构造参数节点
        List<Element> subElemList = beanElement.elements();
        // 如果没有构造参数,则直接调用无参构造函数
        if (subElemList.size() == 0) {
            // 获取无参构造函数
            Constructor<?> constructor = cls.getDeclaredConstructor();
            // 通过无参构造函数,实例化一个对象
            Object beanObj = constructor.newInstance();
            // 将bean对象存放到容器中
            beanMap.put(id, beanObj);
            return;
        }
        Class<?>[] parameterTypes = new Class[subElemList.size()];
        Object[] argsObject = new Object[subElemList.size()];
        int i = 0;
        for (Element subElem: subElemList) {
            String ref = subElem.attributeValue("ref");
            argsObject[i] = beanMap.get(ref);
            // 获取参数对象的接口,而不是类,因为构造函数中参数类型是接口
            parameterTypes[i] = argsObject[i].getClass().getInterfaces()[0];
            i++;
        }
        // 根据构造参数获取构造函数
        Constructor<?> constructor = cls.getDeclaredConstructor(parameterTypes);
        // 实例化对象,传入构造参数
        Object beanObj = constructor.newInstance(argsObject);
        beanMap.put(id, beanObj);
    }

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

测试:

    public static void main(String[] args) throws Exception {
        // 通过xml文件,创建IOC容器
        BeanFactory.refresh("com/bailiban/day1/mydi/constructordemo/DI-constructor.xml");
        // 使用getBean方法,从自建的IOC容器中获取对象
        Client client = (Client) BeanFactory.getBean("client");
        // 我们并没有给Client对象设置userService属性,userService的创建是由IOC容器帮我们完成的
        client.userService.add(new User(1001, "Jim"));
    }

用户添加成功:User{id=1001, name='Jim'}

修改业务类

添加含参数的构造函数

public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }
    
    // 省略其他代码    
}

public class Client {

    private UserService userService;

    public Client(UserService userService) {
        this.userService = userService;
    }   
    
    // 省略其他代码
}    

setter方法注入

修改XML文件

xml配置示例:

<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userDao" class="com.bailiban.day1.helloworld.dao.impl.UserDaoImpl" />
    <bean id="userService" class="com.bailiban.day1.helloworld.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao" />
    </bean>
    <bean id="client" class="com.bailiban.day1.mydi.setterdemo.Client">
        <property name="userService" ref="userService" />
    </bean>
</beans>

注意:
配置文件中<property name="userDao" ref="userDao" />表示属性名称和取值。

IOC容器编码

示例代码:

public class BeanFactory {

    // 省略其他代码

    private static void parseBeanElement(Element beanElement) throws Exception {
        String id = beanElement.attributeValue("id");
        String clsName = beanElement.attributeValue("class");
        // 获取Class对象
        Class<?> cls = Class.forName(clsName);
        // 直接调用无参构造函数,实例化一个对象
        Object beanObj = cls.getDeclaredConstructor().newInstance();
        beanMap.put(id, beanObj);

        // 获取属性节点,并调用setter方法设置属性
        List<Element> subElemList = beanElement.elements();
        for (Element subElem: subElemList) {
            // 获取属性名称
            String name = subElem.attributeValue("name");
            // 获取属性值
            String ref = subElem.attributeValue("ref");
            Object refObj = beanMap.get(ref);
            // 根据属性名称构造setter方法名: set + 属性首字母大写 + 属性其他字符,例:setUserDao
            String methodName = "set" + (char)(name.charAt(0) - 32) + name.substring(1);
            // 获取Method对象
            Method method = cls.getDeclaredMethod(methodName, refObj.getClass().getInterfaces()[0]);
            // 调用setter方法,设置对象属性
            method.invoke(beanObj, refObj);
        }
    }

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

测试:

    public static void main(String[] args) throws Exception {
        // 通过xml文件,创建IOC容器
        BeanFactory.refresh("com/bailiban/day1/mydi/setterdemo/DI-setter.xml");
        // 使用getBean方法,从自建的IOC容器中获取对象
        Client client = (Client) BeanFactory.getBean("client");
        // 我们并没有给Client对象设置userService属性,userService的创建是由IOC容器帮我们完成的
        client.userService.add(new User(1001, "Jim"));
    }

用户添加成功:User{id=1001, name='Jim'}

field注入

修改XML文件

xml配置示例:

<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userDao" class="com.bailiban.day1.helloworld.dao.impl.UserDaoImpl" />
    <bean id="userService" class="com.bailiban.day1.helloworld.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao" />
    </bean>
    <bean id="client" class="com.bailiban.day1.mydi.fielddemo.Client">
        <property name="userService" ref="userService" />
    </bean>
</beans>

注意:
配置文件中<property name="userDao" ref="userDao" />表示属性名称和取值。

IOC容器编码

示例代码:

public class BeanFactory {

    // 省略其他代码

    private static void parseBeanElement(Element beanElement) throws Exception {
        String id = beanElement.attributeValue("id");
        String clsName = beanElement.attributeValue("class");
        // 获取Class对象
        Class<?> cls = Class.forName(clsName);
        // 直接调用无参构造函数,实例化一个对象
        Object beanObj = cls.getDeclaredConstructor().newInstance();
        beanMap.put(id, beanObj);

        // 获取属性节点,并调用setter方法设置属性
        List<Element> subElemList = beanElement.elements();
        for (Element subElem: subElemList) {
            // 获取属性名称
            String name = subElem.attributeValue("name");
            // 获取属性值
            String ref = subElem.attributeValue("ref");
            Object refObj = beanMap.get(ref);
            // 根据属性名称获取Field对象
            Field field = cls.getDeclaredField(name);
            // 调用field的set方法,设置对象属性
            field.setAccessible(true);
            field.set(beanObj, refObj);
        }
    }
}

测试:

    public static void main(String[] args) throws Exception {
        // 通过xml文件,创建IOC容器
        BeanFactory.refresh("com/bailiban/day1/mydi/fielddemo/DI-field.xml");
        // 使用getBean方法,从自建的IOC容器中获取对象
        Client client = (Client) BeanFactory.getBean("client");
        // 我们并没有给Client对象设置userService属性,userService的创建是由IOC容器帮我们完成的
        client.userService.add(new User(1001, "Jim"));
    }

用户添加成功:User{id=1001, name='Jim'}

猜你喜欢

转载自www.cnblogs.com/cheng18/p/12053511.html