中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

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

發布時間:2020-06-26 07:54:05 來源:網絡 閱讀:137610 作者:葉止水ryo 欄目:編程語言

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,我們的程序就是由若干對象組成的。

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

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

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

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

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

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

這個時候我們發現,我們類之間的關系都由 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 是什么

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

清原| 华宁县| 平舆县| 夏邑县| 陆丰市| 札达县| 丹东市| 玛曲县| 孟州市| 拜泉县| 正安县| 永吉县| 资兴市| 北川| 九台市| 青铜峡市| 锡林郭勒盟| 蒲城县| 交城县| 福安市| 沂水县| 云浮市| 陆川县| 小金县| 青川县| 马山县| 平昌县| 冷水江市| 东兰县| 祁阳县| 贵港市| 偃师市| 新野县| 中阳县| 岳普湖县| 托克逊县| 彭山县| 团风县| 陵川县| 永登县| 罗江县|