javascript
Spring IoC 源码系列(五)getBean 流程分析
一、FactoryBean 用法講解
在分析源碼流程之前,我們先來看一下 FactoryBean,乍一看這家伙和 BeanFactory 很像,它們都可以用來獲取 bean 對象,簡單來說 FactoryBean 是一種可以生產(chǎn) bean 的 bean,而 FactoryBean 是一個生產(chǎn) bean 的工廠。
下面舉個例子來簡單說明一下 BeanFactory 的用法:
// 定義一個 User@Dat@Builderpublic class User {private String name;private Integer age;}// 定義一個 FactoryBean 用來創(chuàng)建 User 實public class UserFactoryBean implements FactoryBean<User> {@Overridepublic User getObject() {return User.builder().name("jas").age(18).build();}@Overridepublic Class<?> getObjectType() {return User.class;}}// 在 spring 的配置文件中配置 FactoryBean 實<bean id="userFactoryBean" class="com.jas.mess.factory.UserFactoryBean"/>// 編寫測試類@Testpublic void factoryBeanTest() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);System.out.println("userFactoryBean -> " + applicationContext.getBean("userFactoryBean", User.class));// & 可以用于獲取 FactoryBean 本身UserFactoryBean userFactoryBean = applicationContext.getBean("&userFactoryBean", UserFactoryBean.class);System.out.println("&userFactoryBean -> " + userFactoryBean);System.out.println("userFactoryBean objectType -> " + userFactoryBean.getObjectType());}輸出結(jié)果如下:
從上面的輸出結(jié)果中可以看出 FactoryBean 可以用來創(chuàng)建其他類型的 bean,如果想要獲取 FactoryBean 實例本身,需要給 beanName 加上 & 前綴。
從 Spring IoC 容器獲取 bean 的流程里有關(guān)于 FactoryBean 的處理,上面知道了 FactoryBean 的用法,下面一起來看下具體的流程細(xì)節(jié)吧!
二、getBean 流程分析
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {/*** 這里的 name 有三種情況:* 1.beanName 本身,即:<id="" class=""/> 中 id 的值* 2.'&' 開頭,表示獲取 FactoryBean 本身* 3.alias 別名** 如果 beanName 以 `&` 開頭,則去除 `&` 前綴,如果是 alias 則替換為對應(yīng)的 beanName*/final String beanName = transformedBeanName(name);Object bean;// 單例模式的 Bean 在整個過程中只會被創(chuàng)建一次,第一次創(chuàng)建后會將該 Bean 加載到緩存中,再獲取 Bean 就會從緩存中獲取Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {if (logger.isTraceEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.trace("Returning cached instance of singleton bean '" + beanName + "'");}}/*** 注意這個 name 是沒有經(jīng)過處理的,有可能以 `&` 開頭,即獲取 FactoryBean 本身,而不是對應(yīng)的 bean 實例* 當(dāng)然如果是獲取 FactoryBean 本身則會直接返回*/bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {/*** 因為 Spring 只解決單例模式下得循環(huán)依賴,在原型模式下如果存在循環(huán)依賴則會拋出異常* Spring 只能解決單例模式的循環(huán)依賴,為什么呢?因為單例模式下有對象緩存*/if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// 單例緩存中沒有實例,則可能表明該實例還沒有創(chuàng)建或者該實例在父容器中已經(jīng)創(chuàng)建了,所以需要先檢查一次父容器BeanFactory parentBeanFactory = getParentBeanFactory();// parentBeanFactory 不為空且 beanDefinitionMap 中已經(jīng)保存過 beanName 對應(yīng)的 BeanDefinitionif (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// 獲取 name 對應(yīng)的 beanName,如果 name 是以 & 字符開頭,則返回 & + beanNameString nameToLookup = originalBeanName(name);// 如果父類容器為 AbstractBeanFactory ,則調(diào)用 doGetBean 獲取 beanif (parentBeanFactory instanceof AbstractBeanFactory) {return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}// 用明確的 args 從 parentBeanFactory 中,獲取 Bean 對象else if (args != null) {return (T) parentBeanFactory.getBean(nameToLookup, args);}// 用明確的 requiredType 從 parentBeanFactory 中,獲取 Bean 對象else if (requiredType != null) {return parentBeanFactory.getBean(nameToLookup, requiredType);}// 直接使用 beanName 從 parentBeanFactory 獲取 Bean 對象else {return (T) parentBeanFactory.getBean(nameToLookup);}}if (!typeCheckOnly) {// 標(biāo)記 bean 創(chuàng)建完成或?qū)⒁獎?chuàng)建,主要用來緩存markBeanAsCreated(beanName);}try {// 合并父 BeanDefinition 與子 BeanDefinition(父子容器根據(jù) beanName 進(jìn)行屬性合并)final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);// 檢查給定的合并的 beanDefinitioncheckMergedBeanDefinition(mbd, beanName, args);// 獲取依賴的 beanString[] dependsOn = mbd.getDependsOn();/*** 每個 Bean 都不一定是單獨工作的,它可能會依賴其他 Bean,其他 Bean 也會依賴它* 對于依賴的 Bean ,它會優(yōu)先加載,所以,在 Spring 的加載順序中,* 在初始化某一個 Bean 的時候,首先會初始化這個 Bean 的依賴** 注意這種依賴不是 bean 屬性相互依賴,與我們常說的循環(huán)依賴是兩種類型*** <bean id="beanA" class="BeanA" depends-on="beanB">* <bean id="beanB" class="BeanB" depends-on="beanA">** 對于上面這種依賴關(guān)系會拋出異常*/if (dependsOn != null) {for (String dep : dependsOn) {// 如果存在循環(huán)依賴,則拋出異常if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}// 注入依賴的 bean 存儲在對應(yīng)的 map 中,記錄彼此依賴的關(guān)系registerDependentBean(dep, beanName);try {// 內(nèi)部通過調(diào)用 doGetBean 方法 加載 depends-on 依賴的 beangetBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}}/*** Spring Bean 的作用域默認(rèn)為 singleton ,當(dāng)然還有其他作用域,如 prototype、request、session 等* 不同的作用域會有不同的初始化策略* 如果是單例模式,因為剛開始是從單例緩存中獲取,如果緩存中不存在,則需要從頭開始加載*/if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {destroySingleton(beanName);throw ex;}});// 處理 FactoryBean 類型的 beanbean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}// 原型模式else if (mbd.isPrototype()) {Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}// 從指定的 scope 下創(chuàng)建 beanelse {String scopeName = mbd.getScope();final Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +"defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);}}}catch (BeansException ex) {cleanupAfterBeanCreationFailure(beanName);throw ex;}}// 檢查 bean 的實際類型是否符合需要的類型if (requiredType != null && !requiredType.isInstance(bean)) {try {T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);if (convertedBean == null) {throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}return convertedBean;}catch (TypeMismatchException ex) {if (logger.isTraceEnabled()) {logger.trace("Failed to convert bean '" + name + "' to required type '" +ClassUtils.getQualifiedName(requiredType) + "'", ex);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}}return (T) bean;}getBean 的主要流程就是上面這些,下面根據(jù)步驟來總結(jié)一下,對于一些比較重要的方法,下面會進(jìn)行詳細(xì)分析。
下面對一些重要的方法單獨拿出來分析,幫助大家理解。
2.1getSingleton
protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;}getSingleton 方法涉及到循環(huán)依賴問題,有興趣的可以找下上篇文章,有詳細(xì)解釋。這里涉及到三個緩存 map,其中有一個就是用來解決循環(huán)依賴的,這里分別介紹一下。
- singletonObjects:緩存實例化完成的單例 bean切屬性被注入
- earlySingletonObjects:緩存實例化完成,但還未注入屬性的 bean,用于提前曝光 bean
- singletonFactories:緩存初始化完成,屬性未注入的 bean,當(dāng) bean 提前曝光(緩存到 earlySingletonObjects 中)以后,會刪除該緩存,可以用來解決循環(huán)依賴
2.2 getObjectForBeanInstance
在一開始的時候我們先了解了 FactoryBean,下面的邏輯就會處理到。
protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {// 判斷 nam 是否以 '&' 開頭,如果是表示獲取 FactoryBean 本身,而不是對應(yīng)的 bean 實例if (BeanFactoryUtils.isFactoryDereference(name)) {// 如果是 NullBean 直接返回if (beanInstance instanceof NullBean) {return beanInstance;}// 已 & 開頭,如果不是 FactoryBean 類型拋出異常if (!(beanInstance instanceof FactoryBean)) {throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());}}// 該實例可能是會是一個正常的 bean 又或者是一個 FactoryBean 本身,如果是則直接返回if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {return beanInstance;}Object object = null;/*** 如果 beanDefinition 為 null,則從 factoryBeanObjectCache 緩存中獲取 bean* FactoryBean 生成的單例 bean 會被緩存在 factoryBeanObjectCache 集合中,不用每次都創(chuàng)建*/if (mbd == null) {object = getCachedObjectForFactoryBean(beanName);}// 如果代碼執(zhí)行這里則可以確認(rèn),beanInstance 一定是 FactoryBean 類型if (object == null) {// 轉(zhuǎn)換成 FactoryBean 類型FactoryBean<?> factory = (FactoryBean<?>) beanInstance;// Caches object obtained from FactoryBean if it is a singleton.if (mbd == null && containsBeanDefinition(beanName)) {// 將存儲 XML 配置文件的 GenericBeanDefinition 轉(zhuǎn)換為 RootBeanDefinition,// 合并 BeanDefinitionmbd = getMergedLocalBeanDefinition(beanName);}// 檢測是用戶定義的還是程序本身定義的boolean synthetic = (mbd != null && mbd.isSynthetic());// 從 FactoryBean 中獲取實例object = getObjectFromFactoryBean(factory, beanName, !synthetic);}return object;}getObjectFromFactoryBean 方法還有一些流程,這里就不贅述了,有興趣的自己可以研究下。
參考
Spring IOC 容器源碼分析 - 獲取單例 bean by 田小波
總結(jié)
以上是生活随笔為你收集整理的Spring IoC 源码系列(五)getBean 流程分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: etc必须办理吗
- 下一篇: Spring IoC 源码导读