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

IOC核心理論


1、IOC 理論概要


實體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的區別:

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

Spinrg IOC 容器設計


IOC設計原理與實現


1、 Bean的構建過程

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

BeanDefinition (Bean定義)

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

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

(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整個過程。


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

查看BeanDefinitonRead結構

方法說明:

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負責的,其結構如下:


方法說明:

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>

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

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

3 BeanFactory 與 ApplicationContext 區別

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

ApplicationContext 結構圖:


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

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

2.3 BeanDefinition 加載解析與註冊過程


2.4 Bean獲取與創建流程