1 FactoryBean和BeanFactory區(qū)別
在 Spring 中扒腕,有兩個(gè)接口:BeanFactory
和 FactoryBean
因?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
中,BeanFactory
是IOC
容器的核心接口怪嫌,它的職責(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 的方法饭聚; -
BeanDefinitionRegistry
:Spring
配置文件中每一個(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í)際上是容器中 MyFactorybean
的bean
調(diào)用了 getObject()
方法,并將結(jié)果返回事期。
第二次使用 &myFactoryBean
去容器中拿滥壕,才是真正拿到了 MyFactorybean
的 bean。
兩次拿出來的對象當(dāng)然是不一樣的兽泣。
而 &
就是用于區(qū)分到底拿誰的的前綴绎橘。
1.2.3 使用場景
為什么需要 FactoryBean
,它的特殊功能是什么呢唠倦?
Spring
中FactoryBean
最大的應(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é)
-
BeanFactory
:IOC
容器俱箱,并且提供方法支持外部程序?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
容器管理的 -
BeanFactory
和FactoryBean
都可以用來創(chuàng)建對象肪康,只不過創(chuàng)建的流程和方式不同- 當(dāng)使用
BeanFactory
的時(shí)候荚恶,必須要嚴(yán)格的遵守bean
的生命周期,經(jīng)過一系列繁雜的步驟之后可以創(chuàng)建出單例對象磷支,是流水線式的創(chuàng)建過程 -
FactoryBean
是用戶可以自定義bean
對象的創(chuàng)建流程谒撼,不需要按照bean
的生命周期來創(chuàng)建
- 當(dā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)系
BeanFactory
和ApplicationContext
接口及其子類圖
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
ApplicationContext
由BeanFactory
派生而來悼院,提供了更多面向?qū)嶋H應(yīng)用的功能伤为。在BeanFactory
中,很多功能需要以編程的方式實(shí)現(xiàn)据途,而在ApplicationContext
中則可以通過配置的方式實(shí)現(xiàn)绞愚。
ApplicationContext
的主要實(shí)現(xiàn)類是ClassPathXmlApplicationContext
和FileSystemXmlApplicationContext
,前者默認(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