一、目的
spring管理的bean實例分爲兩類,一類是實現FactoryBean接口的Bean,一類是其它類型的Bean。getObjectForBeanInstance目的是爲了從FactoryBean類型的bean實例中獲取正確的bean。
二、FactoryBean的使用方法
一般類型的bean實例化時,spring通過反射機制利用bean標籤中的class屬性來實例化一個bean,然後利用bean標籤中配置的property子節點,來設置bean對象的屬性值。這種方式在創建bean有複雜的邏輯時,就顯得略微乏力。下面看一下FactoryBean的功能。
- 實現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;
}
}
- 在applicationContext.xml中配置bean屬性
<bean id="myPersonFactoryBean" class="com.kaka.spring.pojo.MyFactoryBean">
<property name="personInfo" value="test factory bean"/>
</bean>
- 從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加載流程
- 獲取用戶傳入name對應的beanName
- 嘗試從緩存中獲取bean實例
- 緩存中不存在,根據找到對應的BeanDefinition信息,加載bean實例
- 從bean實例中獲取真正的對象(本節解析)
- 轉換對象類型
- 返回對象實例
四、相關類及方法
- 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()方法裏面。