Spring核心之FactoryBean堂淡,BeanFactory,ApplicationContext的區(qū)別

1 FactoryBean和BeanFactory區(qū)別

在 Spring 中扒腕,有兩個(gè)接口:BeanFactoryFactoryBean 因?yàn)槊窒嘟苋菀妆换煜倨搿D撬麄冎g有什么區(qū)別呢

1.1 BeanFactory

1.1.1 定義

BeanFactory,以Factory結(jié)尾袜匿,表示它是一個(gè)工廠類(接口)更啄, 它負(fù)責(zé)生產(chǎn)和管理bean的一個(gè)工廠
BeanFactory定義了IOC容器的最基本形式,并提供了IOC容器應(yīng)遵守的的最基本的接口居灯,也就是Spring IOC所遵守的最底層最基本的編程規(guī)范祭务。
Spring中,BeanFactoryIOC容器的核心接口怪嫌,它的職責(zé)包括:實(shí)例化义锥、定位、配置應(yīng)用程序中的對象及建立這些對象間的依賴

Spring代碼中岩灭,BeanFactory只是個(gè)接口拌倍,并不是IOC容器的具體實(shí)現(xiàn),但是Spring容器給出了很多種實(shí)現(xiàn),比如:

  • DefaultListableBeanFactory柱恤。
  • XmlBeanFactory
  • ApplicationContext

這些實(shí)現(xiàn)類從不同的維度對 beanFactory 進(jìn)行了擴(kuò)展数初。

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 的轉(zhuǎn)義標(biāo)識符。(具體用法后面有介紹)
    String FACTORY_BEAN_PREFIX = "&";
        
    根據(jù) name 從容器中拿對應(yīng)的 bean
    Object getBean(String name) throws BeansException;
    
    根據(jù) name 和 type 從容器中拿對應(yīng)的 bean梗顺,要對 bean 的類型做校驗(yàn)泡孩。
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    
    在容器中能否找到與 name 匹配的 bean 或者 beanDefinition。
    boolean containsBean(String name);
    
    判斷 name 對對應(yīng)的 bean 是不是 單例寺谤。
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    判斷 name 對應(yīng)的 bean 與指定的類型是否匹配仑鸥。
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    
    boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    
    根據(jù) name 獲取對應(yīng)的 bean 的類型。
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    
    根據(jù) name 獲取對應(yīng) bean 的 別名变屁。
    String[] getAliases(String name);
}

1.1.3 BeanFactory體系結(jié)構(gòu)

BeanFactory 的類體系結(jié)構(gòu)

  • BeanFactory: 接口位于類結(jié)構(gòu)樹的頂端眼俊, 它最主要的方法就是getBean(StringbeanName),該方法從容器中返回特定名稱的Bean粟关,BeanFactory 的功能通過其他的接口得到不斷擴(kuò)展泵琳。
  • ListableBeanFactory:該接口定義了訪問容器中Bean 基本信息的若干方法,如查看Bean 的個(gè)數(shù)誊役、獲取某一類型Bean 的配置名、查看容器中是否包括某一Bean 等方法谷市;
  • HierarchicalBeanFactory:父子級聯(lián)IoC 容器的接口蛔垢,子容器可以通過接口方法訪問父容器;
  • ConfigurableBeanFactory:是一個(gè)重要的接口迫悠,增強(qiáng)了IoC 容器的可定制性鹏漆,它定義了設(shè)置類裝載器、屬性編輯器创泄、容器初始化后置處理器等方法艺玲;
  • AutowireCapableBeanFactory:定義了將容器中的Bean 按某種規(guī)則(如按名字匹配、按類型匹配等)進(jìn)行自動裝配的方法鞠抑;
  • SingletonBeanRegistry:定義了允許在運(yùn)行期間向容器注冊單實(shí)例Bean 的方法饭聚;
  • BeanDefinitionRegistrySpring 配置文件中每一個(gè)<bean>節(jié)點(diǎn)元素在Spring 容器里都通過一個(gè)BeanDefinition對象表示,它描述了Bean的配置信息搁拙。而BeanDefinitionRegistry 接口提供了向容器手工注冊BeanDefinition 對象的方法

1.1.4 使用場景

使用場景:

  • 通過 名字或類型從容器中獲取 bean秒梳。
  • 判斷容器中是否包含指定的 bean。
  • 判斷 bean 是不是單例箕速。

1.2 FactoryBean

一般情況下酪碘,Spring通過反射機(jī)制利用<bean>class屬性指定實(shí)現(xiàn)類實(shí)例化Bean,但在某些情況下盐茎,實(shí)例化Bean過程比較復(fù)雜兴垦,如果按照傳統(tǒng)的方式,則需要在<bean>中提供大量的配置信息。配置方式的靈活性是受限的探越,這時(shí)采用編碼的方式可能會得到一個(gè)簡單的方案狡赐。Spring為此提供了一個(gè)org.springframework.bean.factory.FactoryBean的工廠類接口,用戶可以通過實(shí)現(xiàn)該接口定制實(shí)例化Bean的邏輯扶关。

首先 FactoryBean 是一個(gè) bean阴汇,但它又不僅僅是個(gè) bean。它是一個(gè)可以 創(chuàng)建修飾 其他對象的工廠 bean节槐,這跟設(shè)計(jì)模式中的工廠模式或者裝飾模式很相似搀庶,它可以創(chuàng)建除自身以外的其他對象

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 有工廠的味道哥倔。也就是說,如果一個(gè) A類實(shí)現(xiàn)了 FactoryBean 接口揍庄,那么 類A 就變成了 A工廠咆蒿。根據(jù) A 的名稱獲得的實(shí)際上是工廠調(diào)用 getObject() 返回的對象, 而不是 A工廠自己。如果想獲得 A工廠自己的實(shí)例蚂子,需要添加 & 前綴沃测。

1.2.2 用個(gè)demo解釋下

這是一個(gè)簡單的 FactoryBean 的使用

package com.example.demo.domian;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

@Component
public class MyFactoryBean implements FactoryBean { 
  // 保存一句話,用來區(qū)分不同的對象食茎。  
  private String message;
  // 無參構(gòu)造器蒂破。
  public MyFactoryBean() {
      // 意思是:當(dāng)前對象是 MyFactoryBean 的對象。
    this.message = "object of myFactoryBeanSelf";
  }
  // 有參構(gòu)造器别渔。
  public MyFactoryBean(String message) {
    this.message = message;
  }
  // 獲取 message附迷。
  public String getMessage() {
    return this.message;
  }

  @Override
  /**
   *  這個(gè)方法在執(zhí)行時(shí)創(chuàng)建了新的 MyFactoryBean 類型的對象。
   *  這里繼續(xù)沿用了 MyFactoryBean 類型哎媚,但是可以是別的類型
   *  比如:Person喇伯、Car、等等拨与。
   */
  public Object getObject() throws Exception {
      // 意思是:當(dāng)前對象是 MyFactoryBean 的 getObject() 創(chuàng)建的稻据。
    return new MyFactoryBean("object from getObject() of MyFactoryBean");
  }

  @Override
  public Class<?> getObjectType() {
    return MyFactoryBean.class
  }
}

測試內(nèi)容很簡單,MyFactoryBean@Component 注解了,當(dāng)啟動SpringBoot程序的時(shí)候,會為 MyFactoryBean 構(gòu)建 bean 并且保存到容器中椭迎。我們要測試的就是用 類: MyFactoryBean 的 名字: myFactoryBean 去容器中拿 bean宅此,看拿到的結(jié)果是什么?

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));
  }
}
測試結(jié)果:
myBean1 = object from getObject() of MyFactoryBean
myBean2 = object of myFactoryBeanSelf
myBean1.equals(myBean2) = false

結(jié)果很明顯:
第一次使用 myFactoryBean 去容器中拿,實(shí)際上是容器中 MyFactorybeanbean 調(diào)用了 getObject()方法,并將結(jié)果返回事期。
第二次使用 &myFactoryBean 去容器中拿滥壕,才是真正拿到了 MyFactorybean 的 bean。
兩次拿出來的對象當(dāng)然是不一樣的兽泣。
& 就是用于區(qū)分到底拿誰的的前綴绎橘。

1.2.3 使用場景

為什么需要 FactoryBean ,它的特殊功能是什么呢唠倦?
SpringFactoryBean最大的應(yīng)用場景是用在 AOP 中称鳞。我們都知道,AOP 實(shí)際上是 Spring 在運(yùn)行是創(chuàng)建出來的代理對象稠鼻,這個(gè)對象是在運(yùn)行時(shí)才被創(chuàng)建的冈止,而不是在啟動時(shí)定義的,這與工廠方法模式是一致的候齿。更生動地說熙暴,AOP 代理對象通過 java 反射機(jī)制在運(yùn)行時(shí)創(chuàng)建代理對象,并根據(jù)業(yè)務(wù)需求將相應(yīng)的方法編織到代理對象的目標(biāo)方法中慌盯。Spring 中的 ProxyFactoryBean 就是干這事的周霉。
因此,FactoryBean 提供了更靈活的實(shí)例化 bean 的方法亚皂。通過 FactoryBean 我們可以創(chuàng)建更復(fù)雜的 bean

1.3 區(qū)別總結(jié)

  • BeanFactoryIOC 容器俱箱,并且提供方法支持外部程序?qū)@些 bean 的訪問,在程序啟動時(shí) 根據(jù)傳入的參數(shù)產(chǎn)生各種類型的 bean孕讳,并添加到 IOC容器(實(shí)現(xiàn) BeanFactory 接口的類) 的 singletonObject 屬性中。
  • FactoryBean: 首先是個(gè) bean巍膘,也存放在 BeanFactory 中厂财。它具有工廠方法的功能,在程序運(yùn)行中 產(chǎn)生指定(一種)類型的 bean峡懈,并添加到了 IOC容器中的 factoryBeanObjectCache 屬性中璃饱。
    所以,這兩種方式創(chuàng)建的 bean 都是被 spring 容器管理的
  • BeanFactoryFactoryBean都可以用來創(chuàng)建對象肪康,只不過創(chuàng)建的流程和方式不同
    • 當(dāng)使用BeanFactory的時(shí)候荚恶,必須要嚴(yán)格的遵守bean的生命周期,經(jīng)過一系列繁雜的步驟之后可以創(chuàng)建出單例對象磷支,是流水線式的創(chuàng)建過程
    • FactoryBean是用戶可以自定義bean對象的創(chuàng)建流程谒撼,不需要按照bean的生命周期來創(chuàng)建

轉(zhuǎn)載于:https://blog.csdn.net/yy_diego/article/details/115710104

2 BeanFactory和ApplicationContext區(qū)別

BeanFactory已經(jīng)在上面說過了,下面就不再贅述了

2.1 ApplicationContext

2.1.1 和BeanFactory關(guān)系

BeanFactoryApplicationContext 接口及其子類圖

image.png

2.1.2 定義

繼承了BeanFactory接口雾狈,擁有BeanFactory的全部功能廓潜,并且擴(kuò)展了很多高級特性,每次容器啟動時(shí)就會創(chuàng)建所有的對象。

創(chuàng)建ApplicationContext的方法:

  • 從類路徑下加載配置文件:ClassPathXmlApplicationContext("applicationContext.xml");
  • 從硬盤絕對路徑下加載配置文件: FileSystemXmlApplicationContext(“d:/xxx/yyy/xxx”);

2.1.3 ApplicationContext類體系結(jié)構(gòu)

Bean 工廠(com.springframework.beans.factory.BeanFactory)是Spring框架最核心的接口辩蛋,它提供了高級IOC的配置機(jī)制呻畸。
應(yīng)用上下文(com.springframework.context.ApplicationContext)建立在BeanFactory 基礎(chǔ)之上。
幾乎所有的應(yīng)用場合我們都直接使用ApplicationContext 而非底層的BeanFactory

ApplicationContextBeanFactory 派生而來悼院,提供了更多面向?qū)嶋H應(yīng)用的功能伤为。在BeanFactory中,很多功能需要以編程的方式實(shí)現(xiàn)据途,而在ApplicationContext 中則可以通過配置的方式實(shí)現(xiàn)绞愚。
ApplicationContext 的主要實(shí)現(xiàn)類是ClassPathXmlApplicationContextFileSystemXmlApplicationContext,前者默認(rèn)從類路徑加載配置文件昨凡,后者默認(rèn)從文件系統(tǒng)中裝載配置文件爽醋。

核心接口包括:

  • ApplicationEventPublisher:讓容器擁有發(fā)布應(yīng)用上下文事件的功能,包括容器啟動事件便脊、關(guān)閉事件等蚂四。實(shí)現(xiàn)了ApplicationListener事件監(jiān)聽接口的Bean 可以接收到容器事件, 并對事件進(jìn)行響應(yīng)處理哪痰。在ApplicationContext抽象實(shí)現(xiàn)類AbstractApplicationContext中遂赠,我們可以發(fā)現(xiàn)存在一個(gè)ApplicationEventMulticaster,它負(fù)責(zé)保存所有監(jiān)聽器晌杰,以便在容器產(chǎn)生上下文事件時(shí)通知這些事件監(jiān)聽者
  • MessageSource:為應(yīng)用提供i18n國際化消息訪問的功能跷睦;
  • ResourcePatternResolver : 所有ApplicationContext實(shí)現(xiàn)類都實(shí)現(xiàn)了類似于PathMatchingResourcePatternResolver的功能,可以通過帶前綴的Ant 風(fēng)格的資源文件路徑裝載Spring 的配置文件肋演。
  • LifeCycle:該接口是Spring 2.0 加入的抑诸,該接口提供了start()stop()兩個(gè)方法,主要用于控制異步處理過程爹殊。在具體使用時(shí)蜕乡,該接口同時(shí)被 ApplicationContext實(shí)現(xiàn)及具體Bean實(shí)現(xiàn),ApplicationContext會將start/stop的信息傳遞給容器中所有實(shí)現(xiàn)了該接口的Bean梗夸,以達(dá)到管理和控制JMX层玲、任務(wù)調(diào)度等目的。
  • ConfigurableApplicationContext 擴(kuò)展于ApplicationContext反症,它新增加了兩個(gè)主要的方法:refresh()close()辛块,讓ApplicationContext具有啟動、刷新和關(guān)閉應(yīng)用上下文的能力铅碍。在應(yīng)用上下文關(guān)閉的情況下調(diào)用refresh()即可啟動應(yīng)用上下文润绵,在已經(jīng)啟動的狀態(tài)下,調(diào)用refresh()則清除緩存并重新裝載配置信息胞谈,而調(diào)用close()則可關(guān)閉應(yīng)用上下文授药。這些接口方法為容器的控制管理帶來了便利.

代碼示例:

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 結(jié)論

早期的電腦性能低士嚎,內(nèi)存小,所以Spring容器的容量不足悔叽,不能將所有的對象全部創(chuàng)建好放入容器莱衩,所以使用的是BeanFactory,需要某個(gè)對象時(shí)娇澎,再進(jìn)行創(chuàng)建笨蚁,隨著電腦硬件的發(fā)展,內(nèi)存越來越大趟庄,所以Spring框架引入了ApplicationContext括细,將所有的對象都創(chuàng)建好,放入容器戚啥,使用哪個(gè)對象奋单,從容器中取得即可。
所以猫十,web開發(fā)中,使用applicationContext. 在資源匱乏的環(huán)境可以使用BeanFactory

因此览濒,ApplicationContext 的初始化和BeanFactory 有一個(gè)重大的區(qū)別:BeanFactory在初始化容器時(shí),并未實(shí)例化Bean拖云,直到第一次訪問某個(gè)Bean 時(shí)才實(shí)例目標(biāo)Bean贷笛;而ApplicationContext則在初始化應(yīng)用上下文時(shí)就實(shí)例化所有單實(shí)例的Bean

ApplicationContext接口,它由BeanFactory接口派生而來,ApplicationContext包含BeanFactory的所有功能宙项,通常建議比BeanFactory優(yōu)先

轉(zhuǎn)載于:https://blog.csdn.net/qq_20757489/article/details/88543252

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乏苦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子尤筐,更是在濱河造成了極大的恐慌汇荐,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盆繁,死亡現(xiàn)場離奇詭異掀淘,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)改基,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進(jìn)店門繁疤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咖为,“玉大人秕狰,你說我怎么就攤上這事≡耆荆” “怎么了鸣哀?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吞彤。 經(jīng)常有香客問我我衬,道長叹放,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任挠羔,我火速辦了婚禮井仰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘破加。我一直安慰自己俱恶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布范舀。 她就那樣靜靜地躺著合是,像睡著了一般。 火紅的嫁衣襯著肌膚如雪锭环。 梳的紋絲不亂的頭發(fā)上聪全,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天,我揣著相機(jī)與錄音辅辩,去河邊找鬼难礼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛汽久,可吹牛的內(nèi)容都是我干的鹤竭。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼景醇,長吁一口氣:“原來是場噩夢啊……” “哼臀稚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起三痰,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤吧寺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后散劫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稚机,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年获搏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赖条。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,664評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡常熙,死狀恐怖纬乍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情裸卫,我是刑警寧澤仿贬,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站墓贿,受9級特大地震影響茧泪,放射性物質(zhì)發(fā)生泄漏蜓氨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一队伟、第九天 我趴在偏房一處隱蔽的房頂上張望穴吹。 院中可真熱鬧,春花似錦嗜侮、人聲如沸刀荒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缠借。三九已至,卻和暖如春宜猜,著一層夾襖步出監(jiān)牢的瞬間泼返,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工姨拥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绅喉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓叫乌,卻偏偏與公主長得像柴罐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子憨奸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評論 2 349

推薦閱讀更多精彩內(nèi)容