說到spring不得不提其兩大特性IOC、AOP输钩,本文主要介紹結(jié)合代碼看下spring Ioc相關(guān)原理床玻,閱讀源碼如果只是為了看源碼效率就會很低,還是要有一定的目的性屑柔,我們結(jié)合著以下的問題去源碼中找答案黍特。本篇文章為《圖靈學(xué)院》課程筆記
- Bean工廠是如何產(chǎn)生Bean的
- Bean的依賴關(guān)系是由誰來解決的
- Bean工廠和應(yīng)用上下文的區(qū)別
如下圖spring Ioc的整體架構(gòu)圖中可以看出,Spring啟動時讀取bean配置信息锯蛀,并在spring中生成一份相應(yīng)bean配置注冊表灭衷,然后會根據(jù)注冊表實例化Bean,并且裝配好bean 之間的依賴關(guān)系旁涤,為上層應(yīng)用提供bean實例翔曲,其中bean的緩存池是通過hashmap實現(xiàn)的
bean的構(gòu)建過程
spring.xml中保存了Bean的描述配置,BeanFactory讀取這些配置然后生成bean劈愚,這是我們對ioc原理的一般理解瞳遍,深入思考還會有更多問題產(chǎn)生
- 哪個java對象承載了配置信息里的內(nèi)容
- 這些承載對象是讀取配置文件并裝載的
- 這些裝載對象又保存在哪里
BeanDefinition(Bean定義)
ioc 實現(xiàn)中 我們在xml 中描述的Bean信息最后 都將保存至BeanDefinition (定義)對象中,其中xml bean 與BeanDefinition 程一對一的關(guān)系菌羽。xml bean中設(shè)置的屬性最后都會體現(xiàn)在BeanDefinition中
由此可見掠械,xml bean中設(shè)置的屬性最后都會體現(xiàn)在BeanDefinition中。如:
**XML-bean ** | BeanDefinition |
---|---|
class | beanClassName |
scope | scope |
lazy-init | lazyInit |
constructor-arg | ConstructorArgument |
property | MutablePropertyValues |
factory-method | factoryMethodName |
destroy-method | AbstractBeanDefinition.destroyMethodName |
init-method | AbstractBeanDefinition.initMethodName |
autowire | AbstractBeanDefinition.autowireMode |
id | |
name |
BeanDefinition 屬性結(jié)構(gòu)
BeanDefinitionRegistry(Bean注冊器)
在上表中我們并沒有看到 xml bean 中的 id 和name屬性沒有體現(xiàn)在定義中注祖,原因是ID 其作為當(dāng)前Bean的存儲key注冊到了BeanDefinitionRegistry 注冊器中猾蒂。name 作為別名key 注冊到了 AliasRegistry 注冊中心。其最后都是指向其對應(yīng)的BeanDefinition是晨。
BeanDefinitionRegistry屬性結(jié)構(gòu)
BeanDefinitionReader(Bean定義讀榷遣ぁ)
現(xiàn)在我們了解 BeanDefinition 中存儲了Xml Bean信息,而BeanDefinitionRegister 基于ID和name 保存了Bean的定義罩缴。接下要學(xué)習(xí)的是從xml Bean到BeanDefinition 然后在注冊至BeanDefinitionRegister 整個過程蚊逢。
上圖中可以看出Bean的定義是由BeanDefinitionReader 從xml 中讀取配置并構(gòu)建出 BeanDefinitionReader,然后在基于別名注冊到BeanDefinitionRegister中层扶。
BeanDefinitionReader結(jié)構(gòu)
- int loadBeanDefinitions(Resource var1)
基于資源加載beanDefinition并注冊到注冊器
-
int loadBeanDefinitions(String var1)
基于資源路徑加載beanDefinition并注冊大屏注冊器
-
BeanDefinitionRegistry getRegistry()
獲取注冊器
ResourceLoader getResourceLoader()
獲取資源裝載器
基于示例演示BeanDefinitionReader裝載過程
//創(chuàng)建一個簡單注冊器
BeanDefinitionRegistry register = new SimpleBeanDefinitionRegistry();
//創(chuàng)建bean定義讀取器
BeanDefinitionReader reader = new XmlBeanDefinitionReader(register);
// 創(chuàng)建資源讀取器
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
// 獲取資源
Resource xmlResource = resourceLoader.getResource("spring.xml");
// 裝載Bean的定義
reader.loadBeanDefinitions(xmlResource);
// 打印構(gòu)建的Bean 名稱
System.out.println(Arrays.toString(register.getBeanDefinitionNames());
Beanfactory(bean 工廠)
有了Bean的定義就相當(dāng)于有了產(chǎn)品的配方,接下來就是要把這個配方送到工廠進行生產(chǎn)了烙荷。在ioc當(dāng)中Bean的構(gòu)建是由BeanFactory 負責(zé)的镜会。其結(jié)構(gòu)如下:
方法說明:
- getBean(String)
- 基于ID或name 獲取一個Bean
- T getBean(Class<T> requiredType)
- 基于Bean的類別獲取一個Bean(如果出現(xiàn)多個該類的實例,將會報錯终抽。但可以指定 primary=“true” 調(diào)整優(yōu)先級來解決該錯誤 )
- Object getBean(String name, Object... args)
- 基于名稱獲取一個Bean稚叹,并覆蓋默認的構(gòu)造參數(shù)
- boolean isTypeMatch(String name, Class<?> typeToMatch)
- 指定Bean與指定Class 是否匹配
以上方法中重點要關(guān)注getBean,當(dāng)用戶調(diào)用getBean的時候就會觸發(fā) Bean的創(chuàng)建動作拿诸,其是如何創(chuàng)建的呢扒袖?
演示基本BeanFactory獲取一個Bean
#創(chuàng)建Bean堆棧
// 其反射實例化Bean
java.lang.reflect.Constructor.newInstance(Unknown Source:-1)
BeanUtils.instantiateClass()
//基于實例化策略 實例化Bean
SimpleInstantiationStrategy.instantiate()
AbstractAutowireCapableBeanFactory.instantiateBean()
// 執(zhí)行Bean的實例化方法
AbstractAutowireCapableBeanFactory.createBeanInstance()
AbstractAutowireCapableBeanFactory.doCreateBean()
// 執(zhí)行Bean的創(chuàng)建
AbstractAutowireCapableBeanFactory.createBean()
// 緩存中沒有,調(diào)用指定Bean工廠創(chuàng)建Bean
AbstractBeanFactory$1.getObject()
// 從單例注冊中心獲取Bean緩存
DefaultSingletonBeanRegistry.getSingleton()
AbstractBeanFactory.doGetBean()
// 獲取Bean
AbstractBeanFactory.getBean()
// 調(diào)用的客戶類
com.tuling.spring.BeanFactoryExample.main()
Bean創(chuàng)建時序圖:
從調(diào)用過程可以總結(jié)出以下幾點:
- 調(diào)用BeanFactory.getBean() 會觸發(fā)Bean的實例化亩码。
- DefaultSingletonBeanRegistry 中緩存了單例Bean
- Bean的創(chuàng)建與初始化是由AbstractAutowireCapableBeanFactory 完成的季率。
BeanFactory 與 ApplicationContext區(qū)別
BeanFactory 看上去可以去做IOC當(dāng)中的大部分事情,為什么還要去定義一個ApplicationContext 呢描沟?
ApplicationContext 結(jié)構(gòu)圖
從圖中可以看到 ApplicationContext 它由BeanFactory接口派生而來飒泻,因而提供了BeanFactory所有的功能。除此之外context包還提供了以下的功能:
- MessageSource, 提供國際化的消息訪問
- 資源訪問吏廉,如URL和文件
- 事件傳播泞遗,實現(xiàn)了ApplicationListener接口的bean
- 載入多個(有繼承關(guān)系)上下文 ,使得每一個上下文都專注于一個特定的層次席覆,比如應(yīng)用的web層
原文地址
http://cbaj.gitee.io/blog/2020/08/05/Spring%7CIOC%E5%8E%9F%E7%90%86/#more