從零開始手寫 spring ioc 框架,深入學習 spring 源碼

IoC

Ioc 是一款 spring ioc 核心功能簡化實現版本,便於學習和理解原理。

創作目的

使用 spring 很長時間,對於 spring 使用非常頻繁,實際上對於源碼一直沒有靜下心來學習過。

但是 spring 源碼存在一個問題,那就是過於抽象,導致學習起來成本上升。

所以本項目由漸入深,只實現 spring 的核心功能,便於自己和他人學習 spring 的核心原理。

spring 的核心

Spring 的核心就是 spring-beans,後面的一切 spring-boot,spring-cloud 都是建立在這個地基之上。

當別人問你 spring 的時候,希望你可以談談自己對於 spring ioc 自己更深層的見解,而不是網上人云亦云的幾句話。

什麼是 IOC

控制反轉(Inversion of Control,縮寫爲IoC),是面向對象編程中的一種設計原則,可以用來減低計算機代碼之間的耦合度。

其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI)。

通過控制反轉,對象在被創建的時候,由一個調控系統內所有對象的外界實體,將其所依賴的對象的引用傳遞給它。

也可以說,依賴被注入到對象中。

爲什麼需要 IOC

IoC 是解耦的一種方法。

我們知道Java 是一門面向對象的語言,在 Java 中 Everything is Object,我們的程序就是由若干對象組成的。

當我們的項目越來越大,合作的開發者越來越多的時候,我們的類就會越來越多,類與類之間的引用就會成指數級的增長。

mess

這樣的工程簡直就是災難,如果我們引入 Ioc 框架。

由框架來維護類的生命週期和類之間的引用。

我們的系統就會變成這樣:

manager

這個時候我們發現,我們類之間的關係都由 IoC 框架負責維護類,同時將類注入到需要的類中。

也就是類的使用者只負責使用,而不負責維護。

把專業的事情交給專業的框架來完成,大大的減少開發的複雜度。

快速開始

maven 引入

<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>ioc</artifactId>
    <version>0.1.11</version>
</dependency>

測試準備

全部測試代碼,見 test 模塊。

  • Apple.java
public class Apple {

    public void color() {
        System.out.println("Apple color: red. ");
    }

}
  • apple.json

類似於 xml 的配置,我們暫時使用 json 進行配置驗證。

[
{"name":"apple","className":"com.github.houbb.ioc.test.service.Apple"}
]

執行測試

  • 測試
BeanFactory beanFactory = new JsonApplicationContext("apple.json");
Apple apple = (Apple) beanFactory.getBean("apple");
apple.color();
  • 日誌
Apple color: red.

spring 基本實現流程

說明

spring-beans 一切都是圍繞 bean 展開的。

BeanFactory 負責對 bean 進行生命週期的相關管理,本節展示第一小節的簡單實現流程。

spring 核心流程

Spring IoC 主要是以下幾個步驟。

  1. 初始化 IoC 容器。

  2. 讀取配置文件。

  3. 將配置文件轉換爲容器識別對的數據結構(這個數據結構在Spring中叫做 BeanDefinition)

  4. 利用數據結構依次實例化相應的對象

  5. 注入對象之間的依賴關係

BeanDefinition 的抽象

BeanDefinition 是 spring 對 java bean 屬性的一個抽象,經過這一層抽象,配置文件可以是 xml/json/properties/yaml 等任意一種,甚至包括註解掃包。

爲 spring 的拓展帶來極大的靈活性。

本框架考慮到實現的簡單性,初步只實現了 json 和基於註解掃包兩種方式。

後期如果有時間可以考慮添加 xml 的實現,其實更多是 xml 的解析工作量,核心流程已經全部實現。

實現源碼節選

BeanDefinition 相關

包含了對於 java bean 的基本信息抽象。

  • BeanDefinition.java

其默認實現爲 DefaultBeanDefinition.java,就是對接口實現的最基本的 java POJO

參見 DefaultBeanDefinition

/**
 * 對象定義屬性
 * @author binbin.hou
 * @since 0.0.1
 */
public interface BeanDefinition {

    /**
     * 名稱
     * @return 名稱
     * @since 0.0.1
     */
    String getName();

    /**
     * 設置名稱
     * @param name 名稱
     * @since 0.0.1
     */
    void setName(final String name);

    /**
     * 類名稱
     * @return 類名稱
     */
    String getClassName();

    /**
     * 設置類名稱
     * @param className 類名稱
     * @since 0.0.1
     */
    void setClassName(final String className);

}

BeanFactory 核心管理相關

  • BeanFactory.java
/**
 * bean 工廠接口
 * @author binbin.hou
 * @since 0.0.1
 */
public interface BeanFactory {

    /**
     * 根據名稱獲取對應的實例信息
     * @param beanName bean 名稱
     * @return 對象信息
     * @since 0.0.1
     */
    Object getBean(final String beanName);

    /**
     * 獲取指定類型的實現
     * @param beanName 屬性名稱
     * @param tClass 類型
     * @param <T> 泛型
     * @return 結果
     * @since 0.0.1
     */
    <T> T getBean(final String beanName, final Class<T> tClass);

}
  • DefaultBeanFactory.java

爲接口最基礎的實現,源碼如下:

/**
 * bean 工廠接口
 * @author binbin.hou
 * @since 0.0.1
 */
public class DefaultBeanFactory implements BeanFactory {

    /**
     * 對象信息 map
     * @since 0.0.1
     */
    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    /**
     * 對象 map
     * @since 0.0.1
     */
    private Map<String, Object> beanMap = new ConcurrentHashMap<>();

    /**
     * 註冊對象定義信息
     * @since 0.0.1
     */
    protected void registerBeanDefinition(final String beanName, final BeanDefinition beanDefinition) {
        // 這裏可以添加監聽器
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }

    @Override
    public Object getBean(String beanName) {
        Object bean = beanMap.get(beanName);
        if(ObjectUtil.isNotNull(bean)) {
            // 這裏直接返回的是單例,如果用戶指定爲多例,則每次都需要新建。
            return bean;
        }

        // 獲取對應配置信息
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(ObjectUtil.isNull(beanDefinition)) {
            throw new IocRuntimeException(beanName + " not exists in bean define.");
        }

        // 直接根據
        Object newBean = createBean(beanDefinition);
        // 這裏可以添加對應的監聽器
        beanMap.put(beanName, newBean);
        return newBean;
    }

    /**
     * 根據對象定義信息創建對象
     * @param beanDefinition 對象定義信息
     * @return 創建的對象信息
     * @since 0.0.1
     */
    private Object createBean(final BeanDefinition beanDefinition) {
        String className = beanDefinition.getClassName();
        Class clazz = ClassUtils.getClass(className);
        return ClassUtils.newInstance(clazz);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getBean(String beanName, Class<T> tClass) {
        Object object = getBean(beanName);
        return (T)object;
    }

}

其中 ClassUtils 是基於 class 的反射工具類,詳情見 ClassUtils.java

JsonApplicationContext

基於 json 配置文件實現的基本實現,使用方式見開始種的例子代碼。

  • JsonApplicationContext.java
/**
 * JSON 應用上下文
 * @author binbin.hou
 * @since 0.0.1
 */
public class JsonApplicationContext extends DefaultBeanFactory {

    /**
     * 文件名稱
     * @since 0.0.1
     */
    private final String fileName;

    public JsonApplicationContext(String fileName) {
        this.fileName = fileName;

        // 初始化配置
        this.init();
    }

    /**
     * 初始化配置相關信息
     *
     * <pre>
     *  new TypeReference<List<BeanDefinition>>(){}
     * </pre>
     *
     * 讀取文件:https://blog.csdn.net/feeltouch/article/details/83796764
     * @since 0.0.1
     */
    private void init() {
        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
        final String jsonConfig = FileUtil.getFileContent(is);
        List<DefaultBeanDefinition> beanDefinitions = JsonBs.deserializeArray(jsonConfig, DefaultBeanDefinition.class);
        if(CollectionUtil.isNotEmpty(beanDefinitions)) {
            for (BeanDefinition beanDefinition : beanDefinitions) {
                super.registerBeanDefinition(beanDefinition.getName(), beanDefinition);
            }
        }
    }

}

小結

至此,一個最基本的 spring ioc 就基本實現了。

如果你想繼續學習,可以分別參考以下代碼分支。

分支說明

v0.0.1-BeanFactory 基本實現

v0.0.2-ListBeanFactory 基本實現

v0.0.3-單例和延遲加載

v0.0.4-初始化和銷燬方法

v0.0.5-RespCode 添加和代碼優化

v0.0.6-構造器和 factoryMethod 新建對象

v0.0.7-property 屬性設置

v0.0.8-Aware 監聽器及 PostProcessor

v0.0.9-Parent 屬性繼承

v0.1.0-循環依賴檢測

v0.1.1-@Configuration-java 代碼配置

v0.1.2-@Bean-java 對象定義

v0.1.3-@Lazy-@Scope-java 對象屬性配置

v0.1.4-@Import 配置導入

v0.1.5-@Bean 參數構造以及 @Description

v0.1.6-@Autowired 自動裝配註解支持

v0.1.7-@Primary 指定優先級註解

v0.1.8-@Conditional 條件註解支持

v0.1.9-Environment 和 @Profile 實現

v0.1.10-Property 配置文件相關和 @Value/@PropertyResource 實現

v0.1.11-@ComponentScan 文件包掃描支持

拓展閱讀

Java IOC-00-ioc 是什麼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章