FactoryBean的實現原理與作用

FactoryBean與BeanFactory:

這倆貨在拼寫上很是相似,很多同學在看IOC源碼或者其他地方並不能分清有啥區別,前面的IOC源碼中我簡單說過,現在統一簡單來講一下:
  • FactoryBean:是一個Java Bean,但是它是一個能生產對象的工廠Bean,它的實現和工廠模式及修飾器模式很像。比如下:我們把bean比作是人,那麼FactoryBean可以算是一個女人,首先它本身也是一個人,但它能夠生產人。【挺尷尬的比喻】。
  • BeanFactory:這就是一個Factory,是一個IOC容器或者叫對象工廠,它裏面存着很多的bean。還用上面那個比如:如果bean是人,那麼它可以理解成學校,學校裏面很多人,學校管理這麼多的人。

FactoryBean在IOC的應用:

前面我們在看IOC源碼的時候,發現即使我們已經創建出來了對象的實例,還是要走一個方法再去處理下,這裏就是對FactoryBean的處理,因爲它可以產生對象,所以你getBean的時候取到的不是它本身,而是通過它生成的產品。【如果要取它本身,getBean(&+beanName)】 我們先來回憶下IOC源碼中那個處理FactoryBean的簡略代碼:
代碼1.1:AbstractBeanFactory類的dogetBean方法
	protected <T> T doGetBean(
			final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
			throws BeansException {

<span style="white-space:pre">		</span>...
		if (sharedInstance != null && args == null) {
			...
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
<span style="white-space:pre">		</span>	// Create bean instance.
			if (mbd.isSingleton()) {
				sharedInstance = getSingleton(beanName, new ObjectFactory() {
					public Object getObject() throws BeansException {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							destroySingleton(beanName);
							throw ex;
						}
					}
				});
				bean = 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);
			}

			else {
				String scopeName = mbd.getScope();
				final Scope scope = this.scopes.get(scopeName);
				try {
					Object scopedInstance = scope.get(beanName, new ObjectFactory() {
						public Object getObject() throws BeansException {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						}
					});
					bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
				}
			}
		}
<span style="white-space:pre">		</span>...
		return (T) bean;
	}
我們可以看到,無論是直接取單例的bean,還是創建單例、多例、自定義生命週期的bean,都會經過bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);這個方法,我們現在就來看看這裏到底是做了什麼:
代碼1.2:AbstractBeanFactory類的getObjectForBeanInstance方法
protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

		//如果是對FactoryBean的解引用,但bean對象不是FactoryBean,拋出異常
		if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
			throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
		}

		//如果Bean實例不是FactoryBean,或者指定名稱是FactoryBean的解引用,也就是普通的bean調用,則直接返回當前的Bean實例  
		if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
			return beanInstance;
		}
		
		//處理對FactoryBean的調用
		Object object = null;
		if (mbd == null) {
			//從Bean工廠緩存中獲取給定名稱的實例對象
			object = getCachedObjectForFactoryBean(beanName);
		}
		if (object == null) {
			// Return bean instance from factory.
			FactoryBean factory = (FactoryBean) beanInstance;
			//如果從Bean工廠生產的Bean是單態模式的,則緩存
			if (mbd == null && containsBeanDefinition(beanName)) {
				mbd = getMergedLocalBeanDefinition(beanName);
			}
			boolean synthetic = (mbd != null && mbd.isSynthetic());
			//調用FactoryBeanRegistrySupport類的getObjectFromFactoryBean方法,實現FactoryBean生產Bean對象實例的過程  
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
	}
這裏有必要單獨說一下解引用:
Dereference(解引用):一個在C/C++中應用的比較多術語,在C++中,“*”是解引用符號,“&”是引用符號。
解引用:變量所指向的是所引用對象的本身數據,而不是對象的內存地址。
上面的代碼可以看到,對於大多數bean的getBean,一般走到第二步就返回了,也就是說我們創建的Bean對象就是想要的bean,但對於FactoryBean的創建,如果是對內存地址的引用,那麼取到的是它生產的bean,而不是它本身。所以我們繼續看怎麼取到生產的對象的:
代碼1.3:FactoryBeanRegistrySupport類的getObjectFromFactoryBean方法
	// Bean工廠生產Bean實例對象
	protected Object getObjectFromFactoryBean(FactoryBean factory,
			String beanName, boolean shouldPostProcess) {
		// Bean工廠是單態模式,並且Bean工廠緩存中存在指定名稱的Bean實例對象
		if (factory.isSingleton() && containsSingleton(beanName)) {
			synchronized (getSingletonMutex()) {
				// 直接從Bean工廠緩存中獲取指定名稱的Bean實例對象
				Object object = this.factoryBeanObjectCache.get(beanName);
				// Bean工廠緩存中沒有指定名稱的實例對象,則生產該實例對象
				if (object == null) {
					// 調用Bean工廠的getObject方法生產指定Bean的實例對象
					object = doGetObjectFromFactoryBean(factory, beanName,
							shouldPostProcess);
					// 將生產的實例對象添加到Bean工廠緩存中
					this.factoryBeanObjectCache.put(beanName,
							(object != null ? object : NULL_OBJECT));
				}
				return (object != NULL_OBJECT ? object : null);
			}
		}
		// 調用Bean工廠的getObject方法生產指定Bean的實例對象
		else {
			return doGetObjectFromFactoryBean(factory, beanName,
					shouldPostProcess);
		}
	}
	
	//調用Bean工廠的getObject方法生產指定Bean的實例對象  
    private Object doGetObjectFromFactoryBean(  
            final FactoryBean factory, final String beanName, final boolean shouldPostProcess)  
            throws BeanCreationException {  
        Object object;  
        try {  
            if (System.getSecurityManager() != null) {  
                AccessControlContext acc = getAccessControlContext();  
                try {  
                    //實現PrivilegedExceptionAction接口的匿名內置類  
                    //根據JVM檢查權限,然後決定BeanFactory創建實例對象  
                    object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {  
                        public Object run() throws Exception {  
                                //調用BeanFactory接口實現類的創建對象方法  
                                return factory.getObject();  
                            }  
                        }, acc);  
                }  
                catch (PrivilegedActionException pae) {  
                    throw pae.getException();  
                }  
            }  
            else {  
            	//調用BeanFactory接口實現類的創建對象方法  
                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);  
        }  
        //創建出來的實例對象爲null,或者因爲單態對象正在創建而返回null  
        if (object == null && isSingletonCurrentlyInCreation(beanName)) {  
            throw new BeanCurrentlyInCreationException(  
                    beanName, "FactoryBean which is currently in creation returned null from getObject");  
        }  
        //爲創建出來的Bean實例對象添加BeanPostProcessor後置處理器  
        if (object != null && shouldPostProcess) {  
            try {  
                object = postProcessObjectFromFactoryBean(object, beanName);  
            }  
            catch (Throwable ex) {  
                throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex);  
            }  
        }  
        return object;  
    } 
第一個方法就是區分單例還是多例,第二個方法是真真的調用getObject的方法獲得FactoryBean生產的對象。從代碼中可以看到,具體產生Bean的地方時這個getObject方法,Spring爲這個FactoryBean提供了70多個實現,比如Poxy、JDNI、RMI等等。所以我們不再舉例,看個截圖,下面我們將自己動手實現一個FactoryBean。


FactoryBean的舉例理解

我們先來看下FactoryBean的接口定義:
<span style="white-space:pre">	</span>public interface FactoryBean<T> {
		//返回由FactoryBean創建的bean實例,如果isSingleton()返回true,則該實例會放到Spring容器中單實例緩存池中。
	   T getObject() throws Exception;  
	   //返回FactoryBean創建的bean類型。
	   Class<?> getObjectType();
	   //返回由FactoryBean創建的bean實例的作用域是singleton還是prototype。
	   boolean isSingleton();    
	}
所以:FactoryBean的核心就在於通過getObject方法可以獲取的是它所生產的對象,所以我們在Proxy創建代理對象的時候就比較方便。還有一些bean,如果通過配置的方式,會顯得比較麻煩和複雜,那麼這時候適當的採用編碼方式在某些場合下還是挺不錯的。
我們下面就通過一個簡單的例子來體驗下getObject方法【講道理,這裏實際意義不多,重在理解方法含義】
假如:我們有個Person對象,裏面包含 name,address,age。set、get方法不寫了
public class Person {
    private String name;
    private String address;
    private int age;
}
那麼如果我們要在Spring中配置該對象的話,需要這麼配置:
<bean id="personBean" class="com.gh.MavenTest.Person">
	<property name="name" value="gh1" />
	<property name="address" value="address1" />
	<property name="age" value="28" />
</bean>
那麼現在我們可以通過getBean("personBean")來獲取該對象。那麼我們來看下如果通過實現FactoryBean以後該怎麼寫呢?來看下我們的PersonFactoryBean的代碼:
public class PersonFactoryBean implements FactoryBean<Person>{
	
	private String personInfo;
	
	public Person getObject() throws Exception {
		Person person =  new  Person () ;    
        String []  infos =  personInfo.split ( "," ) ;
        person.setName(infos[0]);
        person.setAddress(infos[1]);
        person.setAge(Integer.parseInt(infos[2]));
		return person;
	}

	public Class<Person> getObjectType() {
		return Person.class;
	}

	public boolean isSingleton() {
		return true;
	}
}
我們看到,這裏PersonFactoryBean實現了FactoryBean接口,那麼自然也要實現它定義的方法。這裏我們是通過一個personInfo字符串解析得到Person對象,那麼我們在配置Spring的時候就可以這麼配置:
<bean id="personFactory" class="com.hik.MavenTest.PersonFactory">
	<property name="personInfo" value="gh2,address2,22"></property>
</bean> 
OK,那麼這個時候我們getBean("personFactory")得到的就是Person對象而不是PersonFactoryBean對象。具體原理參考上面在IOC的應用,我們通過bean = getObjectForBeanInstance(sharedInstance, name, beanName, null)這個方法,具體調用到了getObject方法,所以結果很明顯。

通過上面的小案例的代碼,我們可以看到如果一個類實現了FactoryBean接口,那麼getBean得到的不是他本身了,而是它所產生的對象,如果我們希望得到它本身,只需要加上&符號即可。至於FactoryBean的實際應用,需要大家去發現理解,後面如果有機會會繼續聊聊這個東西。




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