Spring BeanFactory和FactoryBean有哪些區(qū)別
一、簡(jiǎn)介
在Spring中,有這么2個(gè)接口:BeanFactory和FactoryBean,名字很相似,很多小伙伴經(jīng)?;煜诿嬖嚨臅r(shí)候也經(jīng)常會(huì)被問(wèn)BeanFactory和FactoryBean兩者的區(qū)別。本篇文章將詳細(xì)介紹它們的區(qū)別,并結(jié)合示例,幫助大家對(duì)BeanFactory和FactoryBean有一個(gè)很好的認(rèn)識(shí)。
二、BeanFactory
BeanFactory是Spring IoC 容器的頂層接口,主要負(fù)責(zé)實(shí)例化、定位、配置應(yīng)用程序中的對(duì)象及建立這些對(duì)象間的依賴。
BeanFactory只是一個(gè)接口,并不是IOC容器的具體實(shí)現(xiàn),但是Spring容器給出了很多種實(shí)現(xiàn),如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,都是附加了某種功能的實(shí)現(xiàn)。BeanFactory主要的實(shí)現(xiàn)類(包括抽象類):
- AbstractBeanFactory:抽象Bean工廠,絕大部分的實(shí)現(xiàn)類,都是繼承于它;
- DefaultListableBeanFactory:Spring默認(rèn)的工廠類;
- XmlBeanFactory:前期使用XML配置用的比較多的時(shí)候用的Bean工廠;
- AbstractXmlApplicationContext:抽象應(yīng)用容器上下文對(duì)象;
- ClassPathXmlApplicationContext:從 xml 的配置文件中獲取 bean 并且管理它們;
BeanFactory是Bean工廠,它是一個(gè)接口,定義如下:
public interface BeanFactory { /** * 區(qū)分FactoryBean實(shí)例,例如,如果bean命名為myJndiObject是一個(gè)FactoryBean,通過(guò)&myJndiObject將返回工廠,而不是由工廠返回的實(shí)例 */ String FACTORY_BEAN_PREFIX = "&"; /** * 返回指定bean的實(shí)例 */ Object getBean(String name) throws BeansException; /** * 返回指定bean的實(shí)例,并指定返回類型 */ <T> T getBean(String name, Class<T> requiredType) throws BeansException; /** * 返回指定bean的實(shí)例,并指定創(chuàng)建bean實(shí)例時(shí)使用的參數(shù) */ Object getBean(String name, Object... args) throws BeansException; /** * 返回的bean實(shí)例唯一匹配給定的對(duì)象類型 */ <T> T getBean(Class<T> requiredType) throws BeansException; /** * 返回的bean實(shí)例唯一匹配給定的對(duì)象類型,并指定創(chuàng)建bean實(shí)例時(shí)使用的參數(shù) */ <T> T getBean(Class<T> requiredType, Object... args) throws BeansException; /** * 返回一個(gè)指定bean提供者 */ <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType); /** * 返回一個(gè)指定bean提供者 */ <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType); /** * 判斷工廠中是否包含給定名稱的bean定義,若有則返回true */ boolean containsBean(String name); /** * 判斷bean的作用域是否是singleton:?jiǎn)卫J? */ boolean isSingleton(String name) throws NoSuchBeanDefinitionException; /** * 判斷bena的作用域是否是prototype:多例模式 */ boolean isPrototype(String name) throws NoSuchBeanDefinitionException; /** * 檢查具有指定名稱的bean是否與指定的類型匹配 */ boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; /** * 檢查具有指定名稱的bean是否與指定的類型匹配 */ boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException; /** * 用給定的名稱確定bean的類型 */ @Nullable Class<?> getType(String name) throws NoSuchBeanDefinitionException; /** * 用給定的名稱確定bean的類型 */ @Nullable Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException; /** * 返回給定bean名稱的所有別名 */ String[] getAliases(String name); }
下面列舉一下ClassPathXmlApplicationContext的使用示例:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-config.xml"); User user = (User) applicationContext.getBean("user"); user.hello();
三、FactoryBean
一般情況下,Spring通過(guò)反射利用bean的class屬性指定實(shí)現(xiàn)類來(lái)實(shí)例化bean。在某些情況下,實(shí)例化bean的過(guò)程比較復(fù)雜,如果按照傳統(tǒng)的方式,則需要在<bean>中提供大量的配置信息,配置方式的靈活性是受限的,這時(shí)采用編碼的方式可能會(huì)得到一個(gè)簡(jiǎn)單的方案。
Spring為此提供了一個(gè)FactoryBean工廠類接口,用戶可以通過(guò)實(shí)現(xiàn)該接口定制實(shí)例化bean的邏輯。
FactoryBean是一個(gè)能生產(chǎn)或修飾對(duì)象生成的工廠Bean,當(dāng)我們實(shí)現(xiàn)了FactoryBean接口,重寫getObject()方法并返回一個(gè)實(shí)例時(shí),Spring會(huì)按照我們指定的內(nèi)容去注冊(cè)Bean,來(lái)達(dá)到定制實(shí)例化Bean的效果。
FactoryBean定義如下:
public interface FactoryBean<T> { String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType"; /** * 返回需要?jiǎng)?chuàng)建的Bean */ @Nullable T getObject() throws Exception; /** * 返回FactoryBean創(chuàng)建的Bean類型 */ @Nullable Class<?> getObjectType(); /** * 是否單例Bean,默認(rèn)是單例的,存入Spring容器中單例緩存池 */ default boolean isSingleton() { return true; } }
當(dāng)配置文件中<bean>的class屬性配置的實(shí)現(xiàn)類是FactoryBean時(shí),通過(guò)getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的對(duì)象,相當(dāng)于FactoryBean#getObject()代理了getBean()方法。
下面我們通過(guò)一個(gè)簡(jiǎn)單的FactoryBean案例,實(shí)現(xiàn)自定義注入Bean對(duì)象:
public class Student implements Serializable { private String id; private String name; private int age; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", age=" + age + '}'; } } @Component public class MyFactoryBean implements FactoryBean<Student> { @Override public Student getObject() throws Exception { Student student = new Student(); student.setId(UUID.randomUUID().toString()); student.setName("張三"); student.setAge(20); return student; } @Override public Class<?> getObjectType() { return Student.class; } @Override public boolean isSingleton() { return true; } }
簡(jiǎn)單測(cè)試一下Spring是否幫助我們創(chuàng)建Student這個(gè)Bean對(duì)象:
public void test2() { System.out.println("========返回工廠中的實(shí)例========"); //返回工廠中的實(shí)例,調(diào)用FactoryBean.getObject()創(chuàng)建實(shí)例 Student student1 = (Student) applicationContext.getBean("myFactoryBean"); System.out.println(student1); System.out.println("========返回工廠本身========"); //返回工廠本身,通過(guò)構(gòu)造方法初始化實(shí)例 Object bean = applicationContext.getBean("&myFactoryBean"); System.out.println(bean); }
運(yùn)行結(jié)果如下:
========返回工廠中的實(shí)例========
Student{id='5aa54f31-3d4a-4bc0-989a-5149f393c3db', name='張三', age=20}
========返回工廠本身========
com.wsh.springtransactiondemo.factorybean.MyFactoryBean@24e95e44
可以看到,根據(jù)"myFactoryBean"的名稱獲取到的實(shí)際上是FactoryBean工廠調(diào)用getObject()返回的對(duì)象,而不是MyFactoryBean工廠本身,如果要獲取MyFactoryBean工廠本身實(shí)例,那么需要在名稱前面加上'&'符號(hào)。如下:
- getBean("myFactoryBean"):返回MyFactoryBean工廠中g(shù)etObject()方法返回的實(shí)例對(duì)象;
- getBean("&myFactoryBean"):返回MyFactoryBean工廠本身的實(shí)例;
為什么要使用FactoryBean?
在某些情況下,對(duì)于實(shí)例Bean對(duì)象比較復(fù)雜的情況下,如果使用傳統(tǒng)方式創(chuàng)建bean會(huì)比較復(fù)雜,如xml配置繁瑣等,于是Spring就提供了FactoryBean接口,讓用戶通過(guò)實(shí)現(xiàn)該接口來(lái)自定義該Bean接口的實(shí)例化過(guò)程。
四、總結(jié)
Spring 中為我們提供了兩種類型的 bean,一種就是普通的 bean,我們通過(guò)getBean(id) 方法獲得是該 bean 的實(shí)際類型,另外還有一種 bean是FactoryBean,也就是工廠 bean,我們通過(guò)getBean(id) 獲得是該工廠所產(chǎn)生的 Bean的實(shí)例,而不是FactoryBean的實(shí)例。
- BeanFactory:Bean工廠,它是一個(gè)用于管理Bean的一個(gè)工廠,在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)來(lái)進(jìn)行管理的;
- FactoryBean:工廠Bean,它是一個(gè)支持我們自定義Bean對(duì)象的工廠;
到此這篇關(guān)于Spring BeanFactory和FactoryBean有哪些區(qū)別的文章就介紹到這了,更多相關(guān)Spring BeanFactory和FactoryBean內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決MyEclipse10.7部署報(bào)錯(cuò)拋空指針異常問(wèn)題的方法
這篇文章主要介紹了解決MyEclipse10.7部署報(bào)錯(cuò)拋空指針異常問(wèn)題的方法,需要的朋友可以參考下2015-12-12Java語(yǔ)言實(shí)現(xiàn)反轉(zhuǎn)鏈表代碼示例
這篇文章主要介紹了Java語(yǔ)言實(shí)現(xiàn)反轉(zhuǎn)鏈表代碼示例,小編覺(jué)得挺不錯(cuò)的,這里分享給大家,供需要的朋友參考。2017-10-10淺談Java設(shè)計(jì)模式之七大設(shè)計(jì)原則
在此之前,我已經(jīng)寫過(guò)很多篇關(guān)于設(shè)計(jì)模式的文章.但都比較草草的理解和簡(jiǎn)單的實(shí)現(xiàn),并未深入理解.為了更加深入感受Java設(shè)計(jì)的魅力,編程的藝術(shù),今天進(jìn)行了七大設(shè)計(jì)原則的學(xué)習(xí)理解,后續(xù)進(jìn)行23種設(shè)計(jì)模式的深入學(xué)習(xí)探究,需要的朋友可以參考下2021-05-05Java虛擬機(jī)JVM性能優(yōu)化(一):JVM知識(shí)總結(jié)
這篇文章主要介紹了Java虛擬機(jī)JVM性能優(yōu)化(一):JVM知識(shí)總結(jié),本文是系列文章的第一篇,后續(xù)篇章請(qǐng)繼續(xù)關(guān)注腳本之家,需要的朋友可以參考下2014-09-09Josephus環(huán)的四種解法(約瑟夫環(huán))基于java詳解
這篇文章主要介紹了Josephus環(huán)的四種解法(約瑟夫環(huán))基于java詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09