手寫一個最精簡的IOC容器,實現對象之間的依賴關係

1.基本概念:

在這以前先介紹Spring及Spring IOC的基本概念:
Spring容器-一個IOC容器,用以管理程序中的各類對象以及他們之間的聯繫。git

  • IOC(Inversion Of Contorl)控制反轉 ——本來是由應用程序管理對象之間的關係,如今把控制權交給了容器,稱之爲控制反轉。通俗來講就是本來咱們建立對象須要使用new,可是咱們如今不直接跟對象打交道了,而是在配置文件中寫好須要一個什麼樣的對象,由容器代替咱們去新建對象。

好比說,我建立這樣一個類:github

public class OrderDao {
    public void selcet(){
        System.out.println("select");
    }
}

複製代碼

而後在resource資源目錄下新建config.xml文件,將配置信息貼入其中:web

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:contex="http://www.springframework.org/schema/context"
       xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- bean definitions here -->
 </beans>
複製代碼

本來我新建一個它的實例須要經過new,可是有了IOC以後,我只須要在config.xml中聲明 <bean id="OrderDao" class="com.github.hcsp.OrderDao"/>而後經過工廠方法獲取:spring

//接受一個config.xml路徑
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:db/mybatis/config.xml");
//最核心的API
OrderDao orderDao = (OrderDao) beanFactory.getBean("OrderDao");
//拿到的這個Bean默認狀況下是單例的既同一時刻只能存在一個類的實例
複製代碼

同時還能夠基於註解聲明類之間存在的依賴關係,好比這種狀況:
bash

public class OrderService {
 
    private OrderDao orderDao;

    public void doSomething() {

        orderDao.selcet();
    }
}
複製代碼

\quad OrderService中存在對OrderDao的引用,本來是須要咱們本身去管理對象之間的依賴關係,可是有了Spring以後,咱們只須要聲明他們之間存在依賴關係,管理他們這種關係的工做就由Spring去完成了。如何實現這個過程呢?這時候要是你直接經過beanFactory去新建OrderService的實例,是沒有這種依賴關係的:服務器

OrderService orderService = (OrderService) beanFactory.getBean("OrderService");
複製代碼

可是我使用@Autowired或者@Resource註解以後

public class OrderService {
    @Resource
    private OrderDao orderDao;

    public void doSomething() {

        orderDao.selcet();
    }
}
複製代碼

接着在<beans>標籤中添加<contex:annotation-config/>,將schemaLocation增長爲:mybatis

xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
複製代碼

狀況就不同了: 架構

\quad能夠看到OrderService中有了OrderDao的實例,而且與原先的是同一個,這足以證實他們之間存在依賴關係。這個過程是經過 依賴注入完成的,要知道在Spring中咱們聲明的對象在剛被建立的時候都是爲null的,依賴注入就是經過接口,構造器等爲對象設置具體的值及對象之間關係的過程。依賴注入是IOC的實現方法之一。

  • Spring MVC——基於Spring和Servlet的web應用框架。是基於Spring可是比Spring更強大的一個應用。
    SpringMVC架構圖:

\quad M-model(模型) 表明數據。
\quad V-view(視圖)表明網頁,JSP等用以展現模型中的數據。
\quad C-controller(控制器)將不一樣的數據顯示在不一樣的視圖上,這個過程由Servlet(小服務器)完成。框架

  • SpringBoot——集成度和自動化程度更高的容器。(內嵌了Servlet)
    SpringBoot封裝程度更高,簡化了各類配置過程,可是使得人們喪失了對底層的控制,更難以瞭解程序的實現細節。

2.手寫一個最精簡的IOC容器

\quad重新建properties文件開始,記得前面講Mybatis的動態SQL配置日誌那裏,新建了一個log4j.properties文件,其中都是一個個的XXX=XXX。這其實相似於HashMap的鍵值對(Properties類繼承了HashTable),前面是對象名,後面是對象的全限定類型。
\quad在咱們的IOC容器中,添加本身的映射。新建一個beans.properties文件,寫入如下數據:
ui

orderDao=com.github.hcsp.ioc.OrderDao
userDao=com.github.hcsp.ioc.UserDao
userService=com.github.hcsp.ioc.UserService
orderService=com.github.hcsp.ioc.OrderService
複製代碼

接下來用這樣一個例子演示IOC加載類之間依賴關係的原理:

\quad在MyIocContainer中加載這個文件:

/**
     * 從.properties文件中獲取信息
     *
     * @return 一個properties類
     */
    public static Properties getAndLoadProperties() {
        Properties properties = new Properties();
        try {
            properties.load(MyIoCContainer.class.getResourceAsStream("/beans.properties"));
        } catch (IOException e) {
            throw new RuntimeException("properties路徑有誤"+e);
        }
        return properties;
    }
複製代碼
/**
     * 遍歷.properties文件中的內容,生成value的實例,將<key,value的實例>逐個映射到HashMap中
     *
     * @param properties 加載後的properties實例
     * @return 映射後的HashMap容器
     */
    public static HashMap<String, Object> newInstance(Properties properties) {
        HashMap<String, Object> hashMap = new HashMap<>();
        properties.forEach((beanName, beanInstance) -> {
            try {
                Class<?> klass = Class.forName((String) beanInstance);
                Object newBeanInstance = klass.getConstructor().newInstance();
                hashMap.put((String) beanName, newBeanInstance);
            } catch (Exception e) {
                throw new RuntimeException();
            }
        });
        return hashMap;
    }
複製代碼
/**
     * 爲帶有@Autowired標籤的成員變量設置依賴關係
     *
     * @param beanName     null
     * @param beanInstance 被依賴的類的全限定類名
     * @param beans        類與類名之間的映射關係
     */
    @SuppressWarnings("unused")
    public static void dependencyInstance(String beanName, Object beanInstance, HashMap<String, Object> beans) {
        List<Field> fields = Stream.of(beanInstance.getClass().getDeclaredFields())
                .filter(field -> field.getAnnotation(Autowired.class) != null)
                .collect(Collectors.toList());
            fields.forEach(field -> {
                String filedName = field.getName();
                Object filedInstance = beans.get(filedName);
                field.setAccessible(true);
                try {
                //爲beanInstance對象設置依賴關係
                    field.set(beanInstance, filedInstance);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            });
    }
複製代碼
// 啓動該容器
    public void start() {
        Properties properties = getAndLoadProperties();
        hashMap = newInstance(properties);
        hashMap.forEach((name, instance) -> {
            dependencyInstance(name, instance, hashMap);
        });
    }

    // 從容器中獲取一個bean
    public Object getBean(String beanName) {
        return hashMap.get(beanName);
    }
複製代碼
public class MyIoCContainer {
    private HashMap<String, Object> hashMap;
    public static void main(String[] args) {
        MyIoCContainer container = new MyIoCContainer();
        container.start();
        OrderService orderService = (OrderService) container.getBean("orderService");
        orderService.createOrder();
      }
複製代碼

運行結果:

\quad其實這就是IOC容器的本質,Spring實現的過程其實與這個過程大同小異。一樣是在XML裏面配置Bean,在咱們本身的例子中Beans的定義就是一個簡單的類名。可是實際上確定比這個複雜,因此在Spring中用一個名爲BeanDefinition的接口去描述,

* A BeanDefinition describes a bean instance, which has property values,
 * constructor argument values, and further information supplied by
 * concrete implementations.
複製代碼

根據描述信息實現BeanDefinition的載入和解析,最後一樣也是Bean的實例化跟依賴注入了。

3.參考資料

相關文章
相關標籤/搜索