一文徹底理解Spring IOC 容器設計理念與源碼解讀

IOC核心理論


1、IOC 理論概要


一文徹底理解Spring IOC 容器設計理念與源碼解讀

一文徹底理解Spring IOC 容器設計理念與源碼解讀

  1. 實體Bean的創建
  • 基於Class 構建
  • 構造方法構建
  • 靜態工廠方法 創建
  • FactoryBean創建

(1) 基於ClassName構建

<code><bean>/<code>

這是最常規的方法,其原理是在spring底層會基於class屬性通過反射進行構建。

(2) 構造方法創建

<code><bean>
<constructor-arg>
<constructor-arg>
/<bean>/<code>

如果需要基於參數進行構建,就採用 構造方法構建,其 對應 屬性如下:

  • name : 構造方法參數變量方法
  • type: 參數類型
  • index :參數索引,從0開始
  • value : 參數值,spring會自動轉換成參數 實際類型值
  • ref: 引用串的 其他對象

(3) 靜態工廠方法創建

<code><bean>
<constructor-arg>
/<bean>/<code>

如果你正在對一個對象進行A/B測試,就可以採用靜態工廠方法的方式創建,其基於策略創建不同的對象或填充不同的屬性。

該模式下必須創建一個靜態工廠方法,並且方法返回該實例,spring會調用該靜態方法創建對象。


<code>public static HelloSpring build(String type) {
if (type.equals("A")) {
return new HelloSpring("luban", "man");
} else if (type.equals("B")) {
return new HelloSpring("diaocan", "woman");
} else {
throw new IllegalArgumentException("type must A or B");
}
}/<code>

(4) FactoryBean創建

<code><bean> 1
<property>
/<bean>/<code>

指定一個Bean工廠來創建對象,對象構建初始化完全交給該工廠來實現。配置Bean時指定該工廠類的一個類名:

<code>public class DriverFactoryBean implements FactoryBean {
private String jdbcUrl;
public Object getObject() throws Exception {
return DriverManager.getDriver(jdbcUrl);
}
public Class> getObjectType() {
return Driver.class;
}
public boolean isSingleton() {
return true;
}
public String getJdbcUrl() {
return jdbcUrl;
}
public void setJdbcUrl(String jdbcUrl) {
this.jdbcUrl = jdbcUrl;
}
}
/<code>

3、bean的基本特性

  • 作用範圍
  • 生命週期
  • 裝載機制

a 作用範圍

很多時候Bean對象是無狀態的,而有些時候又是有裝填的,無狀態的對象我們採用單例即可,而又狀態則必須是多例模式,通過scope即可創建:

<code>scope = "prototype"
scope="singleton"
<bean>/<code>

如果一個Bean設置成prototype,我們可以通過BeanFactoryAware獲取BeanFactory對象,即可每次獲取的都是新對象。

Bean對象的創建、初始化、銷燬即是Bean的聲明週期,通過init-method、destory-method屬性可以分別制指定週期構建方法與 初始方法。

<code><bean>/<code>

如果覺得 麻煩,可以讓Bean去實現InitializingBean.afterPropertiesSet() 、DisposableBean.destroy()方法,分別對應 初始化 和銷燬 方法。

c、加載機制

.指示Bean在何時進行加載。設置lazy-init即可,其值如下:

  • true: 懶加載,即延遲加載
  • false: 非懶加載,容器啟動時即創建對象
  • default: 默認,採用default-lazy-init 中指定值,如果default-lazy-init 沒有指定就是false

什麼時候使用懶加載??

懶加載會使容器啟動的更快,而非懶加載可以時容器啟動時更快的發現程序當中的錯誤,選擇哪一個就是追求的是啟動速度 ,還是希望更早的 發現錯誤,一般我們會選擇後者。


4、依賴注入

spring ioc 如何依賴注入的。有以下幾種方式:

  • set 方法注入
  • 構造方法注入
  • 自動注入(byName、byType)
  • 方法注入(lookup-method)

@Autowired 與 @Resources的區別:

  1. @Autiwired 與 @Resource 都可以用來裝配bean,都可以寫在字段上,或者寫在setter 方法上
  2. @Autiwired 默認按類型裝配(這個註解是屬於spring的),默認情況下必須要求依賴對象必須存在,如果允許為null,可以設置它的required 屬性為false,如:@Autowired(required = false)
  3. @Resource(這個註解屬於J2EE的),默認按照名稱進行裝配,名稱可以通過name屬性進行指定,如果沒有指定name屬性,當註解寫在字段上時,默認取字段名進行安裝名稱查找,如果註解寫
  4. @Autowired是根據類型進行自動裝配的。如果當Spring上下文中存在不止一個UserDao類型的bean時,就會拋出BeanCreationException異常;如果Spring上下文中不存在UserDao類型的bean,也會拋出BeanCreationException異常。

Spinrg IOC 容器設計


一文徹底理解Spring IOC 容器設計理念與源碼解讀

IOC設計原理與實現


1、 Bean的構建過程

spring.xml 文件中保存了我們對bean的描述配置,BeanFactory會讀取這些配置然後生成對應的Bean,這是我們對ioc原理的一般理解。

BeanDefinition (Bean定義)

ioc實現中 我們在xml中描述的Bean的信息 最後都將保存至BeanDefinition(定義)對象中,其中xml bean 與 BeanDefnition 是一對一的關係。

一文徹底理解Spring IOC 容器設計理念與源碼解讀

由此可見,xml bean 中設置的屬性最後都體現在BeanDefinition中。如

一文徹底理解Spring IOC 容器設計理念與源碼解讀

一文徹底理解Spring IOC 容器設計理念與源碼解讀

(1) BeanDefinitionRegistry(Bean註冊器)

在上表中我們並沒有看到xml bean中的id 和 name 屬性沒有體現在定義中,原因是ID其作為當前Bean的存儲key註冊到了BeanDefinitionRegistry註冊器中。name作為別名key註冊到了AliasRegistry註冊中心。其最後都指向其對應的BeanDefinition。

<code>public interface BeanDefinitionRegistry extends AliasRegistry {
void registerBeanDefinition(String var1, BeanDefinition var2)
void removeBeanDefinition(String var1)

BeanDefinition getBeanDefinition(String var1)

boolean containsBeanDefinition(String var1);


String[] getBeanDefinitionNames();

int getBeanDefinitionCount();
boolean isBeanNameInUse(String var1);
}/<code>
  • BeanDefinitionReader(Bean定義讀取)
  • BeanDefinition中存儲了XML bean信息,而BeanDefinitionRegister基於ID和name保存了Bean的定義。接下來學習的是xml bean到 BeanDefiniton,然後再註冊至BeanDefintionRegister整個過程。


    一文徹底理解Spring IOC 容器設計理念與源碼解讀

    上圖中可以看出Bean的定義是由BeanDefinitionReader從xml中讀取配置並並構建出BeanDefinitionReader,然後再基於別名註冊 到BeanDefinitionRegistry中。

  • 查看BeanDefinitonRead結構
  • 一文徹底理解Spring IOC 容器設計理念與源碼解讀

    方法說明:

    • loadBeanDefinitions(Resource resource)
      • 基於資源裝在Bean定義並註冊到註冊器
    • int loadBeanDefinitions(String location)
      • 基於資源路徑裝載Bean定義並註冊至註冊器
    • BeanDefinitionRegistry getRegistry()
      • 獲取註冊器
    • ResourceLoader getResourceLoader()
      • 獲取資源狀態器
  • 基於示例演示BeanDefinitionReader裝載過程
  • <code>//創建一個簡單註冊器
    BeanDefinitionRegistry register = new SimpleBeanDefinitionRegistry();
    //創建bean定義讀取器
    BeanDefinitionReader reader = new XmlBeanDefinitionReader(register);
    // 創建資源讀取器
    DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
    // 獲取資源
    Resource xmlResource = resourceLoader.getResource("spring.xml");
    // 裝載Bean的定義
    reader.loadBeanDefinitions(xmlResource);
    // 打印構建的Bean 名稱
    System.out.println(Arrays.toString(register.getBeanDefinitionNames());/<code>

    Beanfactory(bean工廠)

    有了Bean的定義相當於有了產品的配方,接下來就是要把這個配方送到工廠進行上產了,在ioc當中Bean的構建是由BeanFactory負責的,其結構如下:


    一文徹底理解Spring IOC 容器設計理念與源碼解讀

    方法說明:

    • getBean(String)
      • 基於ID 或 name獲取一個Bean
    • T getBean(Class requiredType)
      • 基於Bean的類別獲取一個Bean(如果出現多個該類的實例,將會報錯。但可以指定primary ="true"調整優先級來解決該錯誤)
    • Object getBean(String name,Object...args)
      • 基於名稱獲取一個Bean,並覆蓋默認的構造參數
    • boolean isTypeMatch(String name,Class> typeToMatch)
      • 指定Bean與指定Class 是否匹配

    以上方法中重點要關注getBean,當用戶調用getBean的時候就會觸發Bean的創建動作,其實如何創建的呢?

    • 演示基本BeanFactory獲取一個Bean
    <code>#創建Bean堆棧
    // 其反射實例化Bean
    java.lang.reflect.Constructor.newInstance(Unknown Source:-1)
    BeanUtils.instantiateClass()
    //基於實例化策略 實例化Bean
    SimpleInstantiationStrategy.instantiate()
    AbstractAutowireCapableBeanFactory.instantiateBean()
    // 執行Bean的實例化方法
    AbstractAutowireCapableBeanFactory.createBeanInstance()
    AbstractAutowireCapableBeanFactory.doCreateBean()

    // 執行Bean的創建
    AbstractAutowireCapableBeanFactory.createBean()
    // 緩存中沒有,調用指定Bean工廠創建Bean
    AbstractBeanFactory$1.getObject()
    // 從單例註冊中心獲取Bean緩存
    DefaultSingletonBeanRegistry.getSingleton()
    AbstractBeanFactory.doGetBean()
    // 獲取Bean
    AbstractBeanFactory.getBean()
    // 調用的客戶類
    com.XXX.spring.BeanFactoryExample.main(
    /<code>
    一文徹底理解Spring IOC 容器設計理念與源碼解讀

    從調用過程可以總結一下幾點:

    1. 調用BeanFactory.getBean()會觸發Bean的實例化
    2. DefaultSingletonBeanRegistry中緩存了單例Bean
    3. Bean的創建於初始化AbstractAutowireCapableBeanFactory完成的

    3 BeanFactory 與 ApplicationContext 區別

    BeanFactory 看上去可以去做 IOC當中的大部分事情,為什麼還要去定義一個ApplicationContext呢??

    ApplicationContext 結構圖:

    一文徹底理解Spring IOC 容器設計理念與源碼解讀


    從上圖中可以看到ApplicationContext 它由BeanFactory接口派生而來,因而提供了BeanFactory的所有的功能。除此之外context包還包含了以下的功上襦:

    1. MessageSource,提供國際化的消息訪問
    2. 資源訪問,如URL和文件
    3. 事件傳播,實現了ApplicationListener接口的Bean
    4. 載入多個上下文,使得每一個上下文都專注於一個特定的層次,比如應用的web層

    2.3 BeanDefinition 加載解析與註冊過程


    一文徹底理解Spring IOC 容器設計理念與源碼解讀

    2.4 Bean獲取與創建流程

    一文徹底理解Spring IOC 容器設計理念與源碼解讀

    一文徹底理解Spring IOC 容器設計理念與源碼解讀


    分享到:


    相關文章: