1. 程式人生 > 其它 >spring原始碼學習之旅(二) 結合原始碼聊聊FactoryBean

spring原始碼學習之旅(二) 結合原始碼聊聊FactoryBean

一、什麼是FactoryBean

FactoryBean是由spring提供的用來讓使用者可以自定bean建立的介面;實現該介面可以讓你的bean不用經過spring複雜的bean建立過程,但同時也能做到拿來即用,按需載入;該介面提供的方法如下:

/**
*  獲取FactoryBean管理的物件的例項 一般由spring自動呼叫 無需手動呼叫
*/
T getObject() throws Exception;

/**
* 獲取FactoryBean管理的物件的型別
*/
Class<?> getObjectType();

/**
* 判斷FactoryBean管理的物件是否是單例  預設為單例
* 如果返回為false,則spring不會使用factoryBeanObjectCache來快取已載入的物件例項,
* 每次都會建立一個全新的物件
*/
default boolean isSingleton() {
		return true;
}

二、FactoryBean在spring中的載入過程

測試環境準備:

  1. 自定義一個FactoryBean實現類,返回對應的測試例項Person類,程式碼如圖:
  2. 為了便於程式碼debug,使用xml的配置方式進行配置,如圖:
  3. 啟動程式碼如下:通過以上環境debug可以發現,當spring容器載入完後在BeanFactory的一級快取singletonObjects中有myFactoryBean而並沒有person類的例項,那ac.getBean("myFactoryBean")是怎麼拿到Person的呢我們可以通過剖析getBean("myFactoryBean")這個方法來進行分析:當spring想要獲取某個bean的時候,首先會從快取中獲取這個bean(參加org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String)),當需要獲取myFactoryBean時
    ,由於xml中有對myFactoryBean的配置所以容器啟動的時候myFactoryBean就已經被存到快取中去了,所以從快取中能直接獲取,接下來會呼叫專門針對載入實現了FactoryBean介面的實現類的方法:getObjectForBeanInstance()==》getObjectFromFactoryBean()==》doGetObjectFromFactoryBean;其中會在doGetObjectFromFactoryBean中呼叫getObject方法返回具體的物件例項,getObjectFromFactoryBean方法中會根據isSingleton方法的具體實現判斷要不要把對應例項載入到factoryBeanObjectCache快取中;下面貼上這個三個核心方法的方法簽名和原始碼:

org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance:

protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
		/**
		 *  Don't let calling code try to dereference the factory if the bean isn't a factory.
		 * 	判斷是否是FactoryBean的子介面,是則設定isFactoryBean為true並返回
		 */
		if (BeanFactoryUtils.isFactoryDereference(name)) {
			if (beanInstance instanceof NullBean) {
				return beanInstance;
			}
			if (!(beanInstance instanceof FactoryBean)) {
				throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
			}
			if (mbd != null) {
				mbd.isFactoryBean = true;
			}
			return beanInstance;
		}

		// Now we have the bean instance, which may be a normal bean or a FactoryBean.
		// If it's a FactoryBean, we use it to create a bean instance, unless the
		// caller actually wants a reference to the factory.
		// 判斷我們拿到的bean例項是不是factoryBean,如果不是直接返回
		if (!(beanInstance instanceof FactoryBean)) {
			return beanInstance;
		}

		Object object = null;
		if (mbd != null) {
			mbd.isFactoryBean = true;
		}
		else {
			// 從factoryBean的快取factoryBeanObjectCache中嘗試獲取bean資訊
			object = getCachedObjectForFactoryBean(beanName);
		}
		if (object == null) {
			// Return bean instance from factory.
			FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
			// Caches object obtained from FactoryBean if it is a singleton.
			if (mbd == null && containsBeanDefinition(beanName)) {
				mbd = getMergedLocalBeanDefinition(beanName);
			}
			boolean synthetic = (mbd != null && mbd.isSynthetic());
			// 呼叫getObject獲取factoryBean管理的bean例項
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
	}

org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getObjectFromFactoryBean:

點選檢視程式碼
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
		if (factory.isSingleton() && containsSingleton(beanName)) {
			synchronized (getSingletonMutex()) {
				Object object = this.factoryBeanObjectCache.get(beanName);
				if (object == null) {
					object = doGetObjectFromFactoryBean(factory, beanName);
					// Only post-process and store if not put there already during getObject() call above
					// (e.g. because of circular reference processing triggered by custom getBean calls)
					Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
					if (alreadyThere != null) {
						object = alreadyThere;
					}
					else {
						if (shouldPostProcess) {
							if (isSingletonCurrentlyInCreation(beanName)) {
								// Temporarily return non-post-processed object, not storing it yet..
								return object;
							}
							beforeSingletonCreation(beanName);
							try {
								object = postProcessObjectFromFactoryBean(object, beanName);
							}
							catch (Throwable ex) {
								throw new BeanCreationException(beanName,
										"Post-processing of FactoryBean's singleton object failed", ex);
							}
							finally {
								afterSingletonCreation(beanName);
							}
						}
						if (containsSingleton(beanName)) {
							this.factoryBeanObjectCache.put(beanName, object);
						}
					}
				}
				return object;
			}
		}
		else {
			Object object = doGetObjectFromFactoryBean(factory, beanName);
			if (shouldPostProcess) {
				try {
					object = postProcessObjectFromFactoryBean(object, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
				}
			}
			return object;
		}
	}

org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBean:

點選檢視程式碼
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
		Object object;
		try {
			if (System.getSecurityManager() != null) {
				AccessControlContext acc = getAccessControlContext();
				try {
					object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
				object = factory.getObject();
			}
		}
		catch (FactoryBeanNotInitializedException ex) {
			throw new BeanCurrentlyInCreationException(beanName, ex.toString());
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
		}

		// Do not accept a null value for a FactoryBean that's not fully
		// initialized yet: Many FactoryBeans just return null then.
		if (object == null) {
			if (isSingletonCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(
						beanName, "FactoryBean which is currently in creation returned null from getObject");
			}
			object = new NullBean();
		}
		return object;
	}

三、FactoryBean和BeanFactory的區別

四、FactoryBean的實際運用場景

希望每get一個知識點都能堅持用部落格記錄下來,加油!