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的實際應用,需要大家去發現理解,後面如果有機會會繼續聊聊這個東西。