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>
BeanDefinition中存儲了XML bean信息,而BeanDefinitionRegister基於ID和name保存了Bean的定義。接下來學習的是xml bean到 BeanDefiniton,然後再註冊至BeanDefintionRegister整個過程。
上圖中可以看出Bean的定義是由BeanDefinitionReader從xml中讀取配置並並構建出BeanDefinitionReader,然後再基於別名註冊 到BeanDefinitionRegistry中。
方法說明:
- loadBeanDefinitions(Resource resource)
- 基於資源裝在Bean定義並註冊到註冊器
- int loadBeanDefinitions(String location)
- 基於資源路徑裝載Bean定義並註冊至註冊器
- BeanDefinitionRegistry getRegistry()
- 獲取註冊器
- ResourceLoader getResourceLoader()
- 獲取資源狀態器
<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中緩存了單例Bean
- Bean的創建於初始化AbstractAutowireCapableBeanFactory完成的
3 BeanFactory 與 ApplicationContext 區別
BeanFactory 看上去可以去做 IOC當中的大部分事情,為什麼還要去定義一個ApplicationContext呢??
ApplicationContext 結構圖:
從上圖中可以看到ApplicationContext 它由BeanFactory接口派生而來,因而提供了BeanFactory的所有的功能。除此之外context包還包含了以下的功上襦:
- MessageSource,提供國際化的消息訪問
- 資源訪問,如URL和文件
- 事件傳播,實現了ApplicationListener接口的Bean
- 載入多個上下文,使得每一個上下文都專注於一個特定的層次,比如應用的web層
2.3 BeanDefinition 加載解析與註冊過程
2.4 Bean獲取與創建流程
閱讀更多 碼農的一天 的文章