1 FactoryBean和BeanFactory差別
在 Spring 中,有兩個接口:BeanFactory 和 FactoryBean 因為名字相近很容易被混淆。那他們之間有什麼差別呢
1.1 BeanFactory
1.1.1 定義
BeanFactory,以Factory結尾,表示它是一個工廠類(接口), 它負責生産和管理bean的一個工廠
BeanFactory定義了IOC容器的最基本形式,并提供了IOC容器應遵守的的最基本的接口,也就是Spring IOC所遵守的最底層和最基本的程式設計規範。
在Spring中,BeanFactory是IOC容器的核心接口,它的職責包括:執行個體化、定位、配置應用程式中的對象及建立這些對象間的依賴
在Spring代碼中,BeanFactory隻是個接口,并不是IOC容器的具體實作,但是Spring容器給出了很多種實作,比如:
- DefaultListableBeanFactory。
- XmlBeanFactory
- ApplicationContext
這些實作類從不同的次元對 beanFactory 進行了擴充。
1.1.2 beanFactory 源碼
package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
public interface BeanFactory {
factoryBean 的轉義辨別符。(具體用法後面有介紹)
String FACTORY_BEAN_PREFIX = "&";
根據 name 從容器中拿對應的 bean
Object getBean(String name) throws BeansException;
根據 name 和 type 從容器中拿對應的 bean,要對 bean 的類型做校驗。
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
在容器中能否找到與 name 比對的 bean 或者 beanDefinition。
boolean containsBean(String name);
判斷 name 對對應的 bean 是不是 單例。
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
判斷 name 對應的 bean 與指定的類型是否比對。
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
根據 name 擷取對應的 bean 的類型。
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
根據 name 擷取對應 bean 的 别名。
String[] getAliases(String name);
}
1.1.3 BeanFactory體系結構
BeanFactory 的類體系結構
- BeanFactory: 接口位于類結構樹的頂端, 它最主要的方法就是getBean(StringbeanName),該方法從容器中傳回特定名稱的Bean,BeanFactory 的功能通過其他的接口得到不斷擴充。
- ListableBeanFactory:該接口定義了通路容器中Bean 基本資訊的若幹方法,如檢視Bean 的個數、擷取某一類型Bean 的配置名、檢視容器中是否包括某一Bean 等方法;
- HierarchicalBeanFactory:父子級聯IoC 容器的接口,子容器可以通過接口方法通路父容器;
- ConfigurableBeanFactory:是一個重要的接口,增強了IoC 容器的可定制性,它定義了設定類裝載器、屬性編輯器、容器初始化後置處理器等方法;
- AutowireCapableBeanFactory:定義了将容器中的Bean 按某種規則(如按名字比對、按類型比對等)進行自動裝配的方法;
- SingletonBeanRegistry:定義了允許在運作期間向容器注冊單執行個體Bean 的方法;
- BeanDefinitionRegistry:Spring 配置檔案中每一個<bean>節點元素在Spring 容器裡都通過一個BeanDefinition對象表示,它描述了Bean的配置資訊。而BeanDefinitionRegistry 接口提供了向容器手工注冊BeanDefinition 對象的方法
1.1.4 使用場景
使用場景:
- 通過 名字或類型從容器中擷取 bean。
- 判斷容器中是否包含指定的 bean。
- 判斷 bean 是不是單例。
1.2 FactoryBean
一般情況下,Spring通過反射機制利用<bean>的class屬性指定實作類執行個體化Bean,但在某些情況下,執行個體化Bean過程比較複雜,如果按照傳統的方式,則需要在<bean>中提供大量的配置資訊。配置方式的靈活性是受限的,這時采用編碼的方式可能會得到一個簡單的方案。Spring為此提供了一個org.springframework.bean.factory.FactoryBean的工廠類接口,使用者可以通過實作該接口定制執行個體化Bean的邏輯。
首先 FactoryBean 是一個 bean,但它又不僅僅是個 bean。它是一個可以 建立 或 修飾 其他對象的工廠 bean,這跟設計模式中的工廠模式或者裝飾模式很相似,它可以建立除自身以外的其他對象
1.2.1 FactoryBean源碼
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
從 factory 中擷取 bean
@Nullable
T getObject() throws Exception;
從 beanFactory 中擷取類型
@Nullable
Class<?> getObjectType();
判斷是單例?
default boolean isSingleton() {
return true;
}
}
從上面的接口可以看出,FactoryBean 有工廠的味道。也就是說,如果一個 A類實作了 FactoryBean 接口,那麼 類A 就變成了 A工廠。根據 A 的名稱獲得的實際上是工廠調用 getObject() 傳回的對象, 而不是 A工廠自己。如果想獲得 A工廠自己的執行個體,需要添加 & 字首。
1.2.2 用個demo解釋下
這是一個簡單的 FactoryBean 的使用
package com.example.demo.domian;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;
@Component
public class MyFactoryBean implements FactoryBean {
// 儲存一句話,用來區分不同的對象。
private String message;
// 無參構造器。
public MyFactoryBean() {
// 意思是:目前對象是 MyFactoryBean 的對象。
this.message = "object of myFactoryBeanSelf";
}
// 有參構造器。
public MyFactoryBean(String message) {
this.message = message;
}
// 擷取 message。
public String getMessage() {
return this.message;
}
@Override
/**
* 這個方法在執行時建立了新的 MyFactoryBean 類型的對象。
* 這裡繼續沿用了 MyFactoryBean 類型,但是可以是别的類型
* 比如:Person、Car、等等。
*/
public Object getObject() throws Exception {
// 意思是:目前對象是 MyFactoryBean 的 getObject() 建立的。
return new MyFactoryBean("object from getObject() of MyFactoryBean");
}
@Override
public Class<?> getObjectType() {
return MyFactoryBean.class
}
}
測試内容很簡單,MyFactoryBean 被 @Component 注解了,當啟動SpringBoot程式的時候,會為 MyFactoryBean 建構 bean 并且儲存到容器中。我們要測試的就是用 類: MyFactoryBean 的 名字: myFactoryBean 去容器中拿 bean,看拿到的結果是什麼?
package com.example.demo.domian;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = com.example.demo.domian.MyFactoryBean.class)
class MyFactoryBeanTest {
@Autowired
private ApplicationContext context;
@Test
public void test() {
// 第一次用 myFactoryBean 去拿。
MyFactoryBean myBean1 = (MyFactoryBean) context.getBean("myFactoryBean");
System.out.println("myBean1 = " + myBean1.getMessage());
// 第二次用 &myFactoryBean 去拿。
MyFactoryBean myBean2 = (MyFactoryBean) context.getBean("&myFactoryBean");
System.out.println("myBean2 = " + myBean2.getMessage());、
// 判斷兩次拿到的對象是不是一樣的?
System.out.println("myBean1.equals(myBean2) = " + myBean1.equals(myBean2));
}
}
測試結果:
myBean1 = object from getObject() of MyFactoryBean
myBean2 = object of myFactoryBeanSelf
myBean1.equals(myBean2) = false
結果很明顯:
第一次使用 myFactoryBean 去容器中拿,實際上是容器中 MyFactorybean的bean 調用了 getObject()方法,并将結果傳回。
第二次使用 &myFactoryBean 去容器中拿,才是真正拿到了 MyFactorybean 的 bean。
兩次拿出來的對象當然是不一樣的。
而 & 就是用于區分到底拿誰的的字首。
1.2.3 使用場景
為什麼需要 FactoryBean ,它的特殊功能是什麼呢?
Spring 中FactoryBean最大的應用場景是用在 AOP 中。我們都知道,AOP 實際上是 Spring 在運作是建立出來的代理對象,這個對象是在運作時才被建立的,而不是在啟動時定義的,這與工廠方法模式是一緻的。更生動地說,AOP 代理對象通過 java 反射機制在運作時建立代理對象,并根據業務需求将相應的方法編織到代理對象的目标方法中。Spring 中的 ProxyFactoryBean 就是幹這事的。
是以,FactoryBean 提供了更靈活的執行個體化 bean 的方法。通過 FactoryBean 我們可以建立更複雜的 bean
1.3 差別總結
- BeanFactory:IOC 容器,并且提供方法支援外部程式對這些 bean 的通路,在程式啟動時 根據傳入的參數産生各種類型的 bean,并添加到 IOC容器(實作 BeanFactory 接口的類) 的 singletonObject 屬性中。
-
FactoryBean: 首先是個 bean,也存放在 BeanFactory 中。它具有工廠方法的功能,在程式運作中 産生指定(一種)類型的 bean,并添加到了 IOC容器中的 factoryBeanObjectCache 屬性中。
是以,這兩種方式建立的 bean 都是被 spring 容器管理的
- BeanFactory和FactoryBean都可以用來建立對象,隻不過建立的流程和方式不同 當使用BeanFactory的時候,必須要嚴格的遵守bean的生命周期,經過一系列繁雜的步驟之後可以建立出單例對象,是流水線式的建立過程 FactoryBean是使用者可以自定義bean對象的建立流程,不需要按照bean的生命周期來建立
轉載于:https://blog.csdn.net/yy_diego/article/details/115710104
2 BeanFactory和ApplicationContext差別
BeanFactory已經在上面說過了,下面就不再贅述了
2.1 ApplicationContext
2.1.1 和BeanFactory關系
BeanFactory和ApplicationContext 接口及其子類圖
2.1.2 定義
繼承了BeanFactory接口,擁有BeanFactory的全部功能,并且擴充了很多進階特性,每次容器啟動時就會建立所有的對象。
建立ApplicationContext的方法:
- 從類路徑下加載配置檔案:ClassPathXmlApplicationContext("applicationContext.xml");
- 從硬碟絕對路徑下加載配置檔案: FileSystemXmlApplicationContext(“d:/xxx/yyy/xxx”);
2.1.3 ApplicationContext類體系結構
Bean 工廠(com.springframework.beans.factory.BeanFactory)是Spring架構最核心的接口,它提供了進階IOC的配置機制。
應用上下文(com.springframework.context.ApplicationContext)建立在BeanFactory 基礎之上。
幾乎所有的應用場合我們都直接使用ApplicationContext 而非底層的BeanFactory
ApplicationContext 由BeanFactory 派生而來,提供了更多面向實際應用的功能。在BeanFactory中,很多功能需要以程式設計的方式實作,而在ApplicationContext 中則可以通過配置的方式實作。
ApplicationContext 的主要實作類是ClassPathXmlApplicationContext 和FileSystemXmlApplicationContext,前者預設從類路徑加載配置檔案,後者預設從檔案系統中裝載配置檔案。
核心接口包括:
- ApplicationEventPublisher:讓容器擁有釋出應用上下文事件的功能,包括容器啟動事件、關閉事件等。實作了ApplicationListener事件監聽接口的Bean 可以接收到容器事件, 并對事件進行響應處理。在ApplicationContext抽象實作類AbstractApplicationContext中,我們可以發現存在一個ApplicationEventMulticaster,它負責儲存所有監聽器,以便在容器産生上下文事件時通知這些事件監聽者
- MessageSource:為應用提供i18n國際化消息通路的功能;
- ResourcePatternResolver : 所有ApplicationContext實作類都實作了類似于PathMatchingResourcePatternResolver的功能,可以通過帶字首的Ant 風格的資源檔案路徑裝載Spring 的配置檔案。
- LifeCycle:該接口是Spring 2.0 加入的,該接口提供了start()和stop()兩個方法,主要用于控制異步處理過程。在具體使用時,該接口同時被 ApplicationContext實作及具體Bean實作,ApplicationContext會将start/stop的資訊傳遞給容器中所有實作了該接口的Bean,以達到管理和控制JMX、任務排程等目的。
- ConfigurableApplicationContext 擴充于ApplicationContext,它新增加了兩個主要的方法:refresh()和close(),讓ApplicationContext具有啟動、重新整理和關閉應用上下文的能力。在應用上下文關閉的情況下調用refresh()即可啟動應用上下文,在已經啟動的狀态下,調用refresh()則清除緩存并重新裝載配置資訊,而調用close()則可關閉應用上下文。這些接口方法為容器的控制管理帶來了便利.
代碼示例:
ApplicationContext ctx =new ClassPathXmlApplicationContext("com/baobaotao/context/beans.xml");
ApplicationContext ctx =new FileSystemXmlApplicationContext("com/baobaotao/context/beans.xml");
ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"conf/beans1.xml","conf/beans2.xml"});
2.2 結論
早期的電腦性能低,記憶體小,是以Spring容器的容量不足,不能将所有的對象全部建立好放入容器,是以使用的是BeanFactory,需要某個對象時,再進行建立,随着電腦硬體的發展,記憶體越來越大,是以Spring架構引入了ApplicationContext,将所有的對象都建立好,放入容器,使用哪個對象,從容器中取得即可。
是以,web開發中,使用applicationContext. 在資源匮乏的環境可以使用BeanFactory
是以,ApplicationContext 的初始化和BeanFactory 有一個重大的差別:BeanFactory在初始化容器時,并未執行個體化Bean,直到第一次通路某個Bean 時才執行個體目标Bean;而ApplicationContext則在初始化應用上下文時就執行個體化所有單執行個體的Bean
ApplicationContext接口,它由BeanFactory接口派生而來,ApplicationContext包含BeanFactory的所有功能,通常建議比BeanFactory優先