spring源碼:bean加載之從bean實例中獲取對象(getObjectForBeanInstance)

一、目的

  spring管理的bean實例分爲兩類,一類是實現FactoryBean接口的Bean,一類是其它類型的Bean。getObjectForBeanInstance目的是爲了從FactoryBean類型的bean實例中獲取正確的bean。

二、FactoryBean的使用方法

一般類型的bean實例化時,spring通過反射機制利用bean標籤中的class屬性來實例化一個bean,然後利用bean標籤中配置的property子節點,來設置bean對象的屬性值。這種方式在創建bean有複雜的邏輯時,就顯得略微乏力。下面看一下FactoryBean的功能。

  1. 實現FactoryBean接口的實體類
@Data
public class MyFactoryBean implements FactoryBean<Person> {
	private String personInfo;
	@Override
	public Person getObject() throws Exception {
		// 該方法內可以編寫較爲複雜的創建邏輯...
		Person person = new Person();
		person.setName("factoryBean");
		person.setNickName(personInfo);
		return person;
	}

	@Override
	public Class<?> getObjectType() {
		return Person.class;
	}
}
  1. 在applicationContext.xml中配置bean屬性
<bean id="myPersonFactoryBean" class="com.kaka.spring.pojo.MyFactoryBean">
    <property name="personInfo" value="test factory bean"/>
</bean>
  1. 從spring工廠中獲取bean
@Test
public void getObjectForBeanInstance(){
	Resource classPathResource = new ClassPathResource("applicationContext.xml");
	BeanFactory xmlBeanFactory = new XmlBeanFactory(classPathResource);
    
    // 獲取的beanName爲bean標籤中的id時,返回的bean爲MyFactoryBean的getObject()方法的返回值
    Person myPerson = xmlBeanFactory.getBean("myPersonFactoryBean", Person.class);
    System.out.println(myPerson);

	// 獲取的beanName爲bean標籤中的id前加上&時,返回的bean爲MyFactoryBean本身
    MyFactoryBean myFactoryBean = xmlBeanFactory.getBean("&myPersonFactoryBean", MyFactoryBean.class);
    System.out.println(myFactoryBean);
}

三、bean加載流程

  1. 獲取用戶傳入name對應的beanName
  2. 嘗試從緩存中獲取bean實例
  3. 緩存中不存在,根據找到對應的BeanDefinition信息,加載bean實例
  4. 從bean實例中獲取真正的對象(本節解析)
  5. 轉換對象類型
  6. 返回對象實例

四、相關類及方法

  • org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean:加載一個Bean的整體過程都在這個方法中
  • org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
  • org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getObjectFromFactoryBean
  • org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBean

五、源碼分析

1. 先看加載bean的方法,AbstractBeanFactory#doGetBean

	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
				@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
		// 1.轉換beanName,主要處理別名、以&開頭的name
		final String beanName = transformedBeanName(name);
		Object bean;
	
		// 2.嘗試從單例緩存中獲取bean實例
		Object sharedInstance = getSingleton(beanName);
		// 3. 獲取bean實例
		// 3.1 緩存中已存在bean實例
		if (sharedInstance != null && args == null) {
			// 省略日誌輸出代碼...
			// 從bean實例中獲取對象(本章重點,獲取實例中的對象)
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}
		else {
			// 省略父工廠處理相關代碼...
			try {
				// 省略dependsOn相關代碼...

				// 3.2 創建單例bean
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							destroySingleton(beanName);
							throw ex;
						}
					});
					// 從bean實例中獲取對象(本章重點,獲取實例中的對象)
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
				// 3.3 創建原型bean實例
				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					// 從bean實例中獲取對象(本章重點,獲取實例中的對象)
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}
				// 3.4 根據scope創建bean實例
				else {
					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實例中獲取對象(本章重點,獲取實例中的對象)
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						// 省略異常處理代碼...
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}
		// 省略其他代碼.
		...
	}

可以看到上面有四種情況:緩存中存在bean實例、創建單例bean、創建原型bean、根據scope創建bean,都調用了getObjectForBeanInstance()方法,接下來我們就點進去看看。

2. 從bean實例的初始狀態獲取對象

name以&開頭,稱爲工廠引用。

	protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
	
		// 1. 如果是工廠引用(即name以&開頭),但該實例又不是FactoryBean類型,則拋出異常
		if (BeanFactoryUtils.isFactoryDereference(name)) {
			if (beanInstance instanceof NullBean) {
				return beanInstance;
			}
			if (!(beanInstance instanceof FactoryBean)) {
				throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
			}
		}

		// 2. 如果該實例不是FactoryBean類型,或者是工廠引用都直接返回該實例
		if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
			return beanInstance;
		}

		Object object = null;
		if (mbd == null) {
			// 嘗試從緩存中加載bean
			object = getCachedObjectForFactoryBean(beanName);
		}
		if (object == null) {
			// 把初始bean實例強轉爲FactoryBean
			FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
			// Caches object obtained from FactoryBean if it is a singleton.
			if (mbd == null && containsBeanDefinition(beanName)) {
				mbd = getMergedLocalBeanDefinition(beanName);
			}
			// 是否是用戶自定義的beanDefinition(默認是false)
			boolean synthetic = (mbd != null && mbd.isSynthetic());
			// 重點方法,跟進去
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
	}

從上面的代碼還看不出從FactoryBean中獲取對象的代碼,繼續看getObjectFromFactoryBean()方法

3. 從FactoryBean中獲取對象:getObjectFromFactoryBean()

	protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
		// 1. 單例模式
		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 {
								// 調用bean的後置處理器(有興趣的可以點進去看下,後面的章節會單獨講述)
								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 {
			// 2. 原型模式
			// 真正獲取對象的方法(重點方法)
			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;
		}
	}

無論是單例還是原型的bean實例,都會調用doGetObjectFromFactoryBean()方法進行真正的獲取邏輯,跟進去看看

4. 真正從FactoryBean中獲取對象的邏輯:doGetObjectFromFactoryBean()

	private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final 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的getObject()方法。

六、借鑑

  • spring中方法調用層級
	protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
		// 省略其他無關代碼...
		object = doGetObjectFromFactoryBean(factory, beanName);
	}

對於某個功能中涉及到許多輔助類的邏輯(緩存、前後置處理等)時,可以把這些輔助類邏輯放到功能方法中;把真正的功能邏輯抽取到doXxx()方法裏面。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章