1. 程式人生 > >使用FactoryBean介面實現自定義bean初始化

使用FactoryBean介面實現自定義bean初始化

使用FactoryBean介面實現自定義bean初始化

本文所要介紹的FactoryBeanSpring中定義的一個介面,當把它的實現類定義為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時預設是單例的,也就意味著FactoryBeangetObject方法在BeanFactory中定義為單例的時候只會呼叫一次。但有一種情況例外,那就是如果定義bean時指定了“lazy-init=true”時,那就意味著該bean只有在用到的時候才會進行初始化,這個時候如果剛好兩個執行緒同時需要使用,就會出現在兩個執行緒中同時呼叫

FactoryBeangetObject方法進行bean的初始化,如不加控制就會出現兩個例項。為保證只有一個例項,getObject方法內部需要是同步的。

此外,需要注意的是FactoryBeanisSingleton方法返回結果表示當前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實現類產生的物件。如在上面示例中,如果我們通過ApplicationContextgetBean(“userFactoryBean”)獲取到的就將是對應產生的User物件,如果我們需要獲取到對應的FactoryBean本身,則可以在對應的FactoryBean實現類定義的bean名稱前加上“&”進行獲取,如上如果我們要獲取到UserFactoryBean本身,則可以通過ApplicationContextgetBean(“&userFactoryBean”)。如果是通過型別獲取,就可以直接通過User型別或者UserFactoryBean型別獲取到對應的bean物件了。

(注:本文是基於Spring3.1.0所寫)