Spring中BeanFactory與FactoryBean到底有什麼區別?
一、BeanFactory
BeanFactory是一個介面,它是Spring中工廠的頂層規範,是SpringIoc容器的核心介面,它定義了getBean()、containsBean()等管理Bean的通用方法。Spring的容器都是它的具體實現如:
DefaultListableBeanFactory
XmlBeanFactory
ApplicationContext
這些實現類又從不同的維度分別有不同的擴充套件。
1.1、原始碼
public interface BeanFactory { //對FactoryBean的轉義定義,因為如果使用bean的名字檢索FactoryBean得到的物件是工廠生成的物件, //如果需要得到工廠本身,需要轉義 String FACTORY_BEAN_PREFIX = "&"; //根據bean的名字,獲取在IOC容器中得到bean例項 Object getBean(String name) throws BeansException; //根據bean的名字和Class型別來得到bean例項,增加了型別安全驗證機制。 <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType, Object... args) throws BeansException; //提供對bean的檢索,看看是否在IOC容器有這個名字的bean boolean containsBean(String name); //根據bean名字得到bean例項,並同時判斷這個bean是不是單例 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; boolean isPrototype(String name) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException; //得到bean例項的Class型別 @Nullable Class<?> getType(String name) throws NoSuchBeanDefinitionException; //得到bean的別名,如果根據別名檢索,那麼其原名也會被檢索出來 String[] getAliases(String name); }
1.2、使用場景
從Ioc容器中獲取Bean(byName or byType)
檢索Ioc容器中是否包含指定的Bean
判斷Bean是否為單例
二、FactoryBean
首先它是一個Bean,但又不僅僅是一個Bean。它是一個能生產或修飾物件生成的工廠Bean,類似於設計模式中的工廠模式和裝飾器模式。它能在需要的時候生產一個物件,且不僅僅限於它自身,它能返回任何Bean的例項。
2.1、原始碼
public interface FactoryBean<T> { //從工廠中獲取bean @Nullable T getObject() throws Exception; //獲取Bean工廠建立的物件的型別 @Nullable Class<?> getObjectType(); //Bean工廠建立的物件是否是單例模式 default boolean isSingleton() { return true; } }
從它定義的介面可以看出,FactoryBean表現的是一個工廠的職責。 即一個Bean A如果實現了FactoryBean介面,那麼A就變成了一個工廠,根據A的名稱獲取到的實際上是工廠呼叫getObject()返回的物件,而不是A本身,如果要獲取工廠A自身的例項,那麼需要在名稱前面加上'&'符號。
getObject('name')返回工廠中的例項
getObject('&name')返回工廠本身的例項
通常情況下,bean 無須自己實現工廠模式,Spring 容器擔任了工廠的 角色;但少數情況下,容器中的 bean 本身就是工廠,作用是產生其他 bean 例項。由工廠 bean 產生的其他 bean 例項,不再由 Spring 容器產生,因此與普通 bean 的配置不同,不再需要提供 class 元素。
2.2、示例
先定義一個Bean實現FactoryBean介面
@Component
public class MyBean implements FactoryBean {
private String message;
public MyBean() {
this.message = "通過構造方法初始化例項";
}
@Override
public Object getObject() throws Exception {
// 這裡並不一定要返回MyBean自身的例項,可以是其他任何物件的例項。
//如return new Student()...
return new MyBean("通過FactoryBean.getObject()建立例項");
}
@Override
public Class<?> getObjectType() {
return MyBean.class;
}
public String getMessage() {
return message;
}
}
MyBean實現了FactoryBean介面的兩個方法,getObject()是可以返回任何物件的例項的,這裡測試就返回MyBean自身例項,且返回前給message欄位賦值。同時在構造方法中也為message賦值。然後測試程式碼中先通過名稱獲取Bean例項,列印message的內容,再通過&+名稱獲取例項並列印message內容。
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class FactoryBeanTest {
@Autowired
private ApplicationContext context;
@Test
public void test() {
MyBean myBean1 = (MyBean) context.getBean("myBean");
System.out.println("myBean1 = " + myBean1.getMessage());
MyBean myBean2 = (MyBean) context.getBean("&myBean");
System.out.println("myBean2 = " + myBean2.getMessage());
System.out.println("myBean1.equals(myBean2) = " + myBean1.equals(myBean2));
}
}
myBean1 = 通過FactoryBean.getObject()初始化例項
myBean2 = 通過構造方法初始化例項
myBean1.equals(myBean2) = false
2.3、使用場景
說了這麼多,為什麼要有FactoryBean這個東西呢,有什麼具體的作用嗎?
FactoryBean在Spring中最為典型的一個應用就是用來建立AOP的代理物件。
我們知道AOP實際上是Spring在執行時建立了一個代理物件,也就是說這個物件,是我們在執行時建立的,而不是一開始就定義好的,這很符合工廠方法模式。更形象地說,AOP代理物件通過Java的反射機制,在執行時建立了一個代理物件,在代理物件的目標方法中根據業務要求織入了相應的方法。這個物件在Spring中就是——ProxyFactoryBean。
所以,FactoryBean為我們例項化Bean提供了一個更為靈活的方式,我們可以通過FactoryBean創建出更為複雜的Bean例項。
三、區別
他們兩個都是個工廠,但FactoryBean本質上還是一個Bean,也歸BeanFactory管理
BeanFactory是Spring容器的頂層介面,FactoryBean更類似於使用者自定義的工廠介面。
總結
BeanFactory與FactoryBean的區別確實容易混淆,死記硬背是不行的,最好還是從原始碼層面,置於spring的環境中去理解。
Spring系列的學習筆記和麵試題,包含spring面試題、spring cloud面試題、spring boot面試題、spring教程筆記、spring boot教程筆記、2020年Java面試手冊。一共整理了1184頁PDF文件。關注公眾號:程式設計師追風,回覆 資料 獲得這份1184頁PDF文件的spring全家桶資料。