1. 程式人生 > 其它 >BeanFactory和FactoryBean

BeanFactory和FactoryBean

BeanFactory和FactoryBean

一,前言

​ 很多java開發者在使用Spring框架中都見過後綴為FactoryBean的類——xxxFactoryBean,比如Mybatis中的SqlSessionFactoryBean。

​ 說到這裡就不得不提BeanFactory。FactoryBean和BeanFactory特別容易讓人混淆,面試還經常問到這兩種概念。其實它們的作用和使用場景是不一樣的。

二,BeanFactory

​ BeanFactory,是一個負責生產和管理bean的一個工廠類(介面)。

​ 在Spring中,BeanFactory是IOC容器的核心介面,它的職責包括:例項化、定位、配置應用程式中的物件及建立這些物件間的依賴。

​ 我們通過getBean()方法,傳入引數——bean的名稱或者型別,便可以從Spring容器中來獲取bean。

​ BeanFactory是用於訪問Spring容器的根介面,是從Spring容器中獲取Bean物件的基礎介面,提供了IOC容器最基本的形式,給具體的IOC容器的實現提供了規範。

​ BeanFactory只是個介面,並不是IOC容器的具體實現,Spring容器給出了很多種 BeanFactory的 擴充套件實現,如:DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等。

​ 我們看BeanFactory的類圖:

​ 從類圖中可以看出,我們更常用的ApplicationContext就是一個BeanFactory。

​ ApplicationContext介面:由BeanFactory介面派生而來,ApplicationContext包含BeanFactory的所有功能,還提供了以下更多的功能:1、MessageSource, 提供國際化的訊息訪問 ;2、資源訪問,如URL和檔案;3、事件傳播。

​ 比較BeanFactory,通常建議優先使用ApplicationContext。

​ 對於BeanFactory這麼介紹相信都不陌生了,讓我們把關注點轉向FactoryBean上。

三,FactoryBean

3.1)什麼是FactoryBean:

​ 和BeanFactory一樣,FactoryBean也是介面。

​ FactoryBean是為IOC容器中的Bean的實現提供了更加靈活的方式,FactoryBean在IOC容器的基礎上,給Bean的實現加上了一個簡單工廠模式和裝飾模式。

​ 一般情況下例項化一個Bean物件:Spring通過反射機制利用的class屬性指定實現類例項化Bean,在某些情況下,例項化Bean過程比較複雜,如果按照傳統的方式,則需要在中提供大量的配置資訊。配置方式的靈活性是受限的,這時採用編碼的方式可能會得到一個簡單的方案。

​ Spring為此提供了一個org.springframework.bean.factory.FactoryBean的工廠類介面,使用者可以通過實現該介面,然後在getObject()方法中靈活配置來定製例項化Bean的邏輯

​ FactoryBean介面對於Spring框架來說佔用重要的地位,Spring自身就提供了70多個FactoryBean的實現——(xxxFactoryBean)。它們隱藏了例項化一些複雜Bean的細節,給上層應用帶來了便利。

​ 從Spring3.0開始,FactoryBean開始支援泛型,即介面宣告改為FactoryBean的形式。

​ 從BeanFactory及其實現類的getBean()方法中獲取到的Bean物件,實際上是FactoryBean的getObject()方法建立並返回的Bean物件,而不是FactoryBean本身。

3.2)FactoryBean的三個方法:

​ FactoryBean 是個什麼玩意兒呢?來看看原始碼:

public interface FactoryBean<T> {

    //返回的物件例項

    T getObject() throws Exception;

    //Bean的型別

    Class<?> getObjectType();

    //true是單例,false是非單例  

    boolean isSingleton();

}

方法一,getObject():

​ getObject():返回Bean物件;

​ 當通過getBean()方法獲取到一個Bean時,返回的並不是xxxFactoryBean本身,而是其建立的Bean物件;

​ 如果要獲取xxxFactoryBean物件本身,請在引數前面加一個&符號來獲取,即:getBean(&BeanName)。

例如:

從 getBean()方法 到 getObject()方法:

​ 從BeanFactory及其實現類的getBean()方法中獲取到的Bean物件,實際上是FactoryBean的getObject()方法建立並返回的Bean物件,而不是FactoryBean本身

debug示例:程式在getBean()方法中 進入到 getObject()方法

BeanFactory.getBean()方法最終返回的是xxxFactoryBean.getObject()方法中建立並返回的Bean物件:

方法二,getObjectType():返回的Bean物件的型別;

方法三,isSingleton():是否是單例物件,true是單例,false是非單例;

3.3)FactoryBean使用場景

​ FactoryBean 用來生成某一個型別Bean例項,它最大的一個作用是:可以讓我們自定義Bean的建立過程。

​ 比如說你有一些同屬於某一型別的Bean物件需要被建立,但是它們自己有各自的特點,你只需要把他們的特點注入FactoryBean中,就可以生產出屬於該型別的各種例項。

四)示例程式碼:

自己實現一個FactoryBean,功能:用來自定義一個Bean物件(Car)的建立過程。

4.1> 新建一個Car實體類:

@Data
public class Car {
    private String carBrand; //汽車品牌
    private String carPrice; //汽車價格
}

4.2> 新建一個自己的FactoryBean實現,自定義Car物件的生成過程:

@Component

public class MyFactoryBean01 implements FactoryBean<Car> {
    /**

     * 當通過getBean獲取一個FactoryBean時,返回的並不是FactoryBean本身,而是其建立的物件;

     * 如果要獲取FactoryBean物件本身,請在引數前面加一個&符號來獲取。

     */


    @Override
    public Car getObject() throws Exception {

        Car car = new Car();

        car.setCarBrand("賓士");

        car.setCarPrice("999999");

        return car;
}

    /**

     *獲取物件的型別

     */

    @Override
    public Class<Car> getObjectType() {

        return Car.class;

    }



    /**

     *該Bean物件是否是單例模式

     */


    @Override
   public boolean isSingleton() {

        return false;

    }

}

4.3> 測試:

4.3.1,呼叫ApplicationContext的getBean()方法:

@SpringBootTest
public class MyFactoryBean01Test {

    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void test01() throws Exception {

        //呼叫getBean()方法獲取Bean物件:

        Object object = applicationContext.getBean("myFactoryBean01");

    }
}

4.3.2,列印輸出可以發現,ApplicationContext的getBean()方法返回的是一個Car物件:

@Test
public void test01() throws Exception {


      Object object = applicationContext.getBean("myFactoryBean01");

      System.out.println("該物件的型別 = " + object.toString());

}

列印結果:

4.3.3,在getBean()方法的引數裡邊在前邊加上一個&,打印出的是xxxFactoryBean物件本身:

@Test



public void test01() throws Exception {

      Object object = applicationContext.getBean("&myFactoryBean01");
      System.out.println("該物件的型別 = " + object.toString());
}

列印結果:

4.3.4,獲取Car物件資訊:

@Test



public void test01() throws Exception {

	//呼叫getBean()方法獲取Bean物件:

	Object object = applicationContext.getBean("myFactoryBean01");

	System.out.println("該物件的型別 = " + object.toString());
	//將該Bean物件轉為Car:
	Car car= (Car) object;

	//列印Car物件資訊:

	System.out.println("car.getCarBrand() = " + car.getCarBrand());

	System.out.println("car.getCarPrice() = " + car.getCarPrice());



}

列印結果:

4.3.5,換一種方式:先獲取xxxFactoryBean物件本身,再獲取Car物件:

@Test
public void test01() throws Exception {

	//呼叫getBean()方法獲取Bean物件:

	Object object = applicationContext.getBean("&myFactoryBean01");

	System.out.println("該物件的型別 = " + object.toString()+"\r\n");

	//先獲取xxxFactoryBean物件本身:

	MyFactoryBean01 myFactoryBean01 = (MyFactoryBean01) object;

	//獲取Car物件:

	Car car = myFactoryBean01.getObject();

	System.out.println("列印Car物件資訊:car.toString() = " + car.toString());

	//獲取物件型別:

	Class<Car> objectType = myFactoryBean01.getObjectType();

	System.out.println("列印物件型別:objectType.toString() = " + objectType.toString());

	//獲取Car物件是否是單例物件:

	boolean isSingleton = myFactoryBean01.isSingleton();

	System.out.println("列印Car物件是否是單例物件:isSingleton = " + isSingleton);
}

列印結果: