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

IoC

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

創做目的

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

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

因此本項目由漸入深,只實現 spring 的核心功能,便於本身和他人學習 spring 的核心原理。spring

spring 的核心

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

當別人問你 spring 的時候,但願你能夠談談本身對於 spring ioc 本身更深層的看法,而不是網上人云亦云的幾句話。json

什麼是 IOC

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

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

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

也能夠說,依賴被注入到對象中。maven

爲何須要 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 是什麼

相關文章
相關標籤/搜索