手写Spring Ioc(控制反转) 和 DI(依赖注入)

92 阅读2分钟

1、实现思路

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="userDao" class="com.jan.dao.IUserDao"></bean>

    <bean id="iservice" class="com.jan.service.IService">
        <property name="userDao" ref="userDao"></property>
    </bean>
</beans>

1、要实现ioc首先我们需要一个容器,容器我们选择map集合来表示。

2、Ioc实现原理 、解析出上面bean标签中的id和class元素的属性值,其中class的属性值是全类名。拿到全类名后就可以通过反射创建对象实例,在将id属性值作为map的key将创建出来的实例对象添加map容器中。

3、DI依赖注入原理 、解析出property 标签中的name 和 ref,其中name是要进行注入的类中字段的名字,ref是要注入的引用(对象)当然也可以注入基本类型和String类型。主要原理还是通过set方法进行注入,java中set方法的规范约定字段的set方法是“set” + “字段名首字母大写” ,拿到字段名后可以拼接出是“set” + “字段名首字母大写”的方法名,通过反射就可以进行方法调用实现注入。

2、实现代码

模拟持久层

/**
 * @Author: jan
 * @Description: TODO
 * @DateTime: 2022/7/14 9:38
 * @Version 1.0
 **/
public class IUserDao {
    public void save() {
        System.out.println("保存用户");
    }
}

模拟业务层

import com.jan.dao.IUserDao;

/**
 * @Author: jan
 * @Description: TODO
 * @DateTime: 2022/7/14 9:39
 * @Version 1.0
 **/
public class IService {
    private IUserDao userDao;

    public void setUserDao(IUserDao userDao) {
        this.userDao = userDao;
    }

    public void save() {
        userDao.save();
    }
}

1、BeanFactory接口

/**
 * @Author: jan
 * @Description: IOC容器
 * @DateTime: 2022/7/14 9:41
 * @Version 1.0
 **/
public interface BeanFactory {
    public Object getBean(String key);
}

2、ClassPathXmlApplicationContext 实现类

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: jan
 * @Description: IOC容器实现类
 * @DateTime: 2022/7/14 9:43
 * @Version 1.0
 **/
public class ClassPathXmlApplicationContext implements BeanFactory{
    // 容器
    private Map<String,Object> beans = new HashMap<>();

    // 从容器中获取bean
    @Override
    public Object getBean(String key) {
        return beans.get(key);
    }

    public ClassPathXmlApplicationContext(String XmlPath) throws Exception{
        // 读取xml配置
        SAXBuilder saxBuilder = new SAXBuilder();

        // 文档对象类型
        Document document = saxBuilder.build(XmlPath);

        // 获取根元素
        Element rootElement = document.getRootElement();

        // 获取根元素中的所有子元素
        List<Element> elements = rootElement.getChildren();

        // 遍历beans中的bean子元素
        for (Element element : elements) {
            // ---------------------IOC 底层实现代码---------------------
            // 获取属性的值
            String id = element.getAttributeValue("id");
            String aClass = element.getAttributeValue("class");

            // 反射创建实例
            Object instance = Class.forName(aClass).newInstance();
            // 将实例放进容器中进行管理
            beans.put(id,instance);

            // ---------------------IOC 底层实现代码---------------------

            // spring ioc 控制反转功能

            List<Element> list = element.getChildren("property");
            for (Element element1 : list) {
                // ---------------------DI 底层实现代码---------------------
                String name = element1.getAttributeValue("name");
                String ref = element1.getAttributeValue("ref");

                // 从容器中获取要注入的对象
                Object obj = beans.get(ref);

                // 拼接“set” + “字段名首字母大写”的方法名
                String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
                // 反射调用set方法进行依赖注入
                Method method = instance.getClass().getMethod(methodName, obj.getClass());

                // 反射调用setXXX 进行属性注入
                method.invoke(instance,obj);
                // ---------------------DI 底层实现代码---------------------
            }
        }

    }
}

3、进行测试

import com.jan.service.IService;

/**
 * @Author: jan
 * @Description: TODO
 * @DateTime: 2022/7/14 10:04
 * @Version 1.0
 **/
public class Main {
    public static void main(String[] args) throws Exception {
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("D:\dev\project\springioc\ioc\beans.xml");

        IService iservice = (IService) beanFactory.getBean("iservice");

        iservice.save();
    }
}

测试结果

屏幕截图 2022-08-03 002944.png