使用FactoryBean接口實現自定義bean初始化
本文所要介紹的FactoryBean是Spring中定義的一個接口,當把它的實現類定義爲BeanFactory中的一個bean,我們在獲取其對應的bean時實際上獲取的是FactoryBean所包含的那個對象,而不是它本身。我們先來看一下FactoryBean的定義。
public interface FactoryBean<T> {
/**
* 獲取實際要返回的bean對象。
* @return
* @throws Exception
*/
T getObject() throws Exception;
/**
* 獲取返回的對象類型
* @return
*/
Class<?> getObjectType();
/**
* 是否單例
* @return
*/
boolean isSingleton();
}
我們可以看到FactoryBean是使用了泛型的,表示其對應產生的Bean是什麼類型的對象。我們來看一個實現。
public class UserFactoryBean implements FactoryBean<User> {
private User user;
@Override
public User getObject() throws Exception {
if (user == null) {
synchronized (this) {
if (user == null) {
User user = new User();
user.setId(1);
user.setName("張三");
this.user = user;
}
}
}
returnuser;
}
@Override
public Class<?> getObjectType() {
return User.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
上面代碼中定義了一個UserFactoryBean,用以產生一個單例的User對象。可以看到,我們在getObject()方法中使用了同步塊來保證產生的bean永遠是同一個對象。其實這個並不是必須的。在初始化時BeanFactory調用FactoryBean創建bean時就是同步的,而且BeanFactory創建bean時默認是單例的,也就意味着FactoryBean的getObject方法在BeanFactory中定義爲單例的時候只會調用一次。但有一種情況例外,那就是如果定義bean時指定了“lazy-init=true”時,那就意味着該bean只有在用到的時候纔會進行初始化,這個時候如果剛好兩個線程同時需要使用,就會出現在兩個線程中同時調用FactoryBean的getObject方法進行bean的初始化,如不加控制就會出現兩個實例。爲保證只有一個實例,getObject方法內部需要是同步的。
此外,需要注意的是FactoryBean的isSingleton方法返回結果表示當前FactoryBean產生的bean是否是單例形式,即每次請求getObject()方法返回的是否都是同一個bean對象。其實FactoryBean更多的是在Spring內部使用,isSingleton只是用來表示當前返回的bean對象是否可以用BeanFactory緩存的一個標誌。
上面示例對象的Spring配置文件如下:
<bean id="userFactoryBean" class="com.xxx.spring.factorybean.UserFactoryBean" lazy-init="false"/>
對於一個FactoryBean接口實現類定義的bean其實Spring將實例化兩個bean,一個是FactoryBean本身對應的bean,另一個是FactoryBean產生的對象對應的bean。所以當我們在通過註解方式注入一個FactoryBean實例對應的bean時,既可以把它當做一個FactoryBean進行注入,也可以把它當做一個對應產生的實例進行注入。而如果是自己直接從ApplicationContext中獲取的話,則直接通過FactoryBean實現類定義的bean名稱獲取到的是FactoryBean實現類產生的對象。如在上面示例中,如果我們通過ApplicationContext的getBean(“userFactoryBean”)獲取到的就將是對應產生的User對象,如果我們需要獲取到對應的FactoryBean本身,則可以在對應的FactoryBean實現類定義的bean名稱前加上“&”進行獲取,如上如果我們要獲取到UserFactoryBean本身,則可以通過ApplicationContext的getBean(“&userFactoryBean”)。如果是通過類型獲取,就可以直接通過User類型或者UserFactoryBean類型獲取到對應的bean對象了。
(注:本文是基於Spring3.1.0所寫)