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通過反射機制利用
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);
}
列印結果: