Spring IOC-BeanFactory的繼承體系結構

本文主要介紹BeanFactory以及它的各種繼承層級的接口、抽象類及實現類,因為內容很多,所以這里不介紹ApplicationContext繼承體系下的類(雖然ApplicationContext本質上也是BeanFactory,但是畢竟這這是我們平時接觸最多的兩種類別,所以分開說)。
關于ApplicationContext繼承體系結構在《Spring IOC-ApplicationContext的繼承體系結構》一文分享說明。
BeanFactory其實就是Spring的IOC容器,當然了Spring為我們準備了許多種IOC容器來使用,這樣可以方便我們從不同的層面,不同的資源位置,不同的形式的定義信息來建立我們需要的IoC容器。
在鄙人博客的不同的博文中其實都提到了Spring的一個設計理念,就是接口-抽象類-實現類的體系結構,這里多說幾句為什么這么設計,熟悉設計模式的人都知道,這樣的目的是為了提高軟件的可維護性、可擴展性、強復用性。說的文雅一點就是使代碼滿足“對修改關閉、對擴展開放”、“里氏代換”原則 、“依賴倒轉”原則、“接口隔離”原則、“合成\聚合復用”原則。如果項了解這幾個原則可以百度下,這里不詳細介紹。
廢話不多說,直接上代碼來看下BeanFactory的繼承道理多屌。
先貼張圖,大家大致看下知道下面介紹的類大概在繼承體系的哪個位置。
在這里插入圖片描述
首先看BeanFactory接口中定義的方法:

public interface BeanFactory {//這里是對FactoryBean的轉義定義,因為如果使用bean的名字檢索FactoryBean得到的對象是工廠生成的對象String FACTORY_BEAN_PREFIX = "&";//這里根據bean的名字,在IOC容器中得到bean實例,這個IOC容器就是一個大的抽象工廠。 Object getBean(String name) throws BeansException;//這里根據bean的名字和Class類型來得到bean實例,和上面的方法不同在于它會拋出異常:如果根據名字取得的bean實例的Class類型和需要的不同的話。<T> T getBean(String name, Class<T> requiredType);<T> T getBean(Class<T> requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException;//這里提供對bean的檢索,看看是否在IOC容器有這個名字的beanboolean containsBean(String name);//這里根據bean名字得到bean實例,并同時判斷這個bean是不是單件 boolean isSingleton(String name) throws NoSuchBeanDefinitionException;//這里根據bean名字得到bean實例,并同時判斷這個bean是不是原型 boolean isPrototype(String name) throws NoSuchBeanDefinitionException;//這里對得到bean實例的Class類型  Class<?> getType(String name) throws NoSuchBeanDefinitionException;//這里得到bean的別名,如果根據別名檢索,那么其原名也會被檢索出來  String[] getAliases(String name);

然后在看BeanFactory 的直接繼承接口(二級接口),有HierarchicalBeanFactory、AutowireCapableBeanFactory和ListableBeanFactory看這三個類代碼:

HierarchicalBeanFactory
作用:是為了實現bean工廠的層級關系提供支持,其中聲明兩個方法:

//得到父工廠
BeanFactory getParentBeanFactory();
//在本地工廠中有沒有給定名稱的bean,不包括繼承的工廠
boolean containsLocalBean(String name);

AutowireCapableBeanFactory
作用:提供自動裝配bean能力的功能支持,聲明方法如下:(這個接口中所有聲明的方法都是在默認的實現實在AbstractAutowireCapableBeanFactory類中默認實現)

//用個給定的class類型制造一個完整的bean
<T> T createBean(Class<T> beanClass) throws BeansException;
//bean初始化完成之后執行回調函數和后處理器,
void autowireBean(Object existingBean) throws BeansException;
// 自動注入和設置bean的屬性、執行factory回調函數比如setBeanName和setBeanFactory和執行bean的所有的后處理器
Object configureBean(Object existingBean, String beanName) throws BeansException;
//調用bean的init方法,這個方法是客戶配置的,在bean實例化之后調用
Object initializeBean(Object existingBean, String beanName) throws BeansException;
//初始化完成之后應用后處理器
Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
//應用前處理器
Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName);

ListableBeanFactory
作用:可以枚舉所有的bean實例,是為了使客戶端訪問工廠中的bean而設計的,主要方法(這些方法顧名思義,所有的方法實現在StaticListableBeanFactory、AbstractApplicationContext和DefaultListableBeanFactory中):

//是否含有給定的名稱的bean
boolean containsBeanDefinition(String beanName);
int getBeanDefinitionCount();
//得到工廠所有的bean的名稱數組
String[] getBeanDefinitionNames();
String[] getBeanNamesForType(Class<?> type);
//根據給定的類型得到和相應的策略得到所有的bean名稱數組
String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
//根據給定過的類型得到所有該類型的bean,返回的結果是一個Map<bean名稱,bean對象>的形式
<T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException;
//得到給定名稱的bean上的給定注解類型的注解對象
<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType);

下面我們介紹BeanFactory 的三級接口,看繼承關系圖知道是ConfigurableBeanFactory和ConfigurableListableBeanFactory。

ConfigurableBeanFactory
作用: 實現可配置的bean的環境功能,這個接口繼承自HierarchicalBeanFactory所以支持層級關系的工廠,和SingletonBeanRegistry所以肯定支持單例工廠行為,看主要方法代碼(在AbstractBeanFactory類中默認實現):

void setConversionService(ConversionService conversionService);
void setTypeConverter(TypeConverter typeConverter);
//支持自定義bean的作用范圍,可以理解為單例和多例之外的
void registerScope(String scopeName, Scope scope);
//歸并的將給定的name的bean的定義
BeanDefinition getMergedBeanDefinition(String beanName);
//添加處理器
void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);
//下面是3個destory方法
void destroyBean(String beanName, Object beanInstance);
void destroyScopedBean(String beanName);
//只能銷毀所有單例的bean,因為多例的是不歸Spring控制的,是由客戶端控制的
void destroySingletons();

ConfigurableListableBeanFactory
作用:提供可配置的、可訪問的功能,接口中的方法在在DefaultListableBeanFactory默認實現默認實現。

public interface ConfigurableListableBeanFactoryextends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {//凍住之后bean定義就不能在被修改和進行任何的后處理器規定的操作void freezeConfiguration();//確保所有的單例bean都實例化void preInstantiateSingletons() throws BeansException;//BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;//判斷當前的bean是不是作為其它bean的依賴注入的boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor);

上面是接口的繼承體系和說明,下面來介紹接口下抽象類的代碼,在上面的介紹中我們知道就兩個抽象類AbstractAutowireCapableBeanFactory和AbstractBeanFactory,先看繼承關系:

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactoryimplements AutowireCapableBeanFactory {
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

可以知道AbstractAutowireCapableBeanFactory 是AbstractBeanFactory 類的子類。通過上面的接口和抽象類的介紹我們將研究的重點轉到轉到這兩個抽象類中。
這里我們主要看bean工廠是怎么將xml中的定義信息轉換為互相依賴的bean定義或者初始化為實體對象。
先看在繼承體系偏上的類AbstractBeanFactory ,這個類最重要的是對BeanFactory中getBean()的實現,直接看實現的代碼:

………………
public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {return doGetBean(name, requiredType, args, false);
}

在看doGetBean()方法代碼:

………………
//首先從單例工廠得到有沒有
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);//不是但單例的
else {//看是不是在父BeanFactory ,因為實現了HierarchicalBeanFactory接口BeanFactory parentBeanFactory = getParentBeanFactory();if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {return parentBeanFactory.getBean(nameToLookup, requiredType);}
……………………if (mbd.isSingleton()) {//單例的//用回調的形式建造bean并且放入單例工廠sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {public Object getObject() throws BeansException {try {return createBean(beanName, mbd, args);}……………………//多例的else if (mbd.isPrototype()) {Object prototypeInstance = null;beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);………………else {//沒有明確是單例的還是不是,有自己的作用域scopeString scopeName = mbd.getScope();final Scope scope = this.scopes.get(scopeName);Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {public Object getObject() throws BeansException {				return createBean(beanName, mbd, args);……………………

可以上面是分為三種情況得到bean的,單例的、多例的、Scope的。但是所有的情況最終都定位到一個方法——createBean(beanName, mbd, args),這個方法在AbstractBeanFactory 是抽象的protected abstract Object createBean(,很明顯,只能在子類中實現,自然就要看AbstractAutowireCapableBeanFactory 中的建造bean的createBean()代碼:

protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)throws BeanCreationException {………………//如果沒有找到這個name的class類型是會拋出異常的resolveBeanClass(mbd, beanName);//判斷在xml中定義的overrides方法存在mbd.prepareMethodOverrides();………………//用bean的前處理器去實例化Object bean = resolveBeforeInstantiation(beanName, mbd);………………Object beanInstance = doCreateBean(beanName, mbd, args);………………

好的,代碼定位到**doCreateBean(beanName, mbd, args)**方法:

//逐漸的構造一個bean,分別用factory method, and autowiring a constructor.去構造,這些都是在xml中配置的。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {………………instanceWrapper = createBeanInstance(beanName, mbd, args);………………//構造bean并且注入依賴所有bean的屬性值Object exposedObject = bean;try {populateBean(beanName, mbd, instanceWrapper);if (exposedObject != null) {//調用配置的init方法exposedObject = initializeBean(beanName,     exposedObject, mbd);} }

代碼定位到createBeanInstance(beanName, mbd, args)

//用 factory method, constructor autowiring, or simple instantiation.三種方法去實例化一個bean
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {………………//用getFactoryMethodName去實例化一個if (mbd.getFactoryMethodName() != null)  {return instantiateUsingFactoryMethod(beanName, mbd, args);}………………//用構造函數if (autowireNecessary) {return autowireConstructor(beanName, mbd, null, null);}else {//用默認的構造函數得到return instantiateBean(beanName, mbd);}………………
}

再定位代碼到populateBean(beanName, mbd, instanceWrapper):

//主要是bean中的成員變量的初始化和注入,《依賴的注入》
protected void populateBean(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw) {……………………if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||     mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs);              // Add property values based on autowire by name if applicable.             if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs);                              }                                                                           // Add property values based on autowire by type if applicable.             if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs);                              }                                                                           pvs = newPvs;                                                               
}                                                                               ……………………//將屬性的引用和具體的對象結合起來,用到了java的反射機制applyPropertyValues(beanName, mbd, bw, pvs);
}

最后是applyPropertyValues(beanName, mbd, bw, pvs)

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {try {//調用BeanWrapper類的方法bw.setPropertyValues(new MutablePropertyValues(deepCopy));}

這里補充一下BeanWrapperImpl類中設置屬性依賴的代碼實現:

………………
//如果有key屬性配置,證明是array  list  或者map
if (tokens.keys != null) {if (propValue.getClass().isArray()) {else if (propValue instanceof List) {else if (propValue instanceof Map) {
}
//普通的屬性設置
else{………………writeMethod.setAccessible(true);writeMethod.invoke(object, value);
}
………………

至此bean的如何初始化和如何進行依賴注入就已經研究代碼完畢。
下面也是最后我們看一下這個繼承體系中具體類XmlBeanFactory、DefaultListableBeanFactory的實現代碼:
XmlBeanFactory

//這個類的實現很簡單,只是委托XmlBeanDefinitionReader進行xml的讀取和配置信息的加載
public class XmlBeanFactory extends DefaultListableBeanFactory {private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);// 構造的時候就進行xml文件的解析public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {super(parentBeanFactory);this.reader.loadBeanDefinitions(resource);}
}

關于XmlBeanFactory加載xml,會在Spring IOC-XmlBeanFactory如何加載xml及如何存儲轉換后的信息一文介紹。

DefaultListableBeanFactory
是XmlBeanFactory的父類,也就是而你看下文章最前面的繼承圖,你會發現DefaultListableBeanFactory擁有這個繼承體系中的所有功能。
那么除了繼承父類的功能方法外,它獨特的功能在于對ConfigurableListableBeanFactory和ListableBeanFactory接口的實現。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/249550.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/249550.shtml
英文地址,請注明出處:http://en.pswp.cn/news/249550.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

deepin15.7掛載/home到單獨的分區:

1、首先打開Gpart分區編輯器&#xff0c;找一個空閑的分區&#xff0c;調整好分區大小&#xff0c;格式化成ext4格式。 具體步驟為首先unmount所用到的盤&#xff0c;然后右擊該盤選擇format to ext4&#xff0c;最后點擊apply提交修改 2、記錄下分區的路徑&#xff0c;比如 /d…

Java使用Redis實現分布式鎖來防止重復提交問題

如何用消息系統避免分布式事務&#xff1f; - 少年阿賓 - BlogJavahttp://www.blogjava.net/stevenjohn/archive/2018/01/04/433004.html 【請求去重】java基于分布式鎖解決重復請求問題 - qq_41793222的博客 - CSDN博客https://blog.csdn.net/qq_41793222/article/details/830…

【PHP】xampp配置多個監聽端口和不同的網站目錄(轉)

轉自&#xff1a;https://blog.csdn.net/cc1314_/article/details/75646344 windows下使用xampp配置多個監聽端口和不同的網站目錄 一&#xff1a;配置Apache文件httpd.conf打開Apache的配置文件httpd.conf&#xff0c;可以通過點擊xampp的Apache的config下的Apache(httpd.conf…

本地連接虛擬機 Oracle數據庫 報ORA-12541:TNS:no listener

一、環境 本機環境&#xff1a;win10,pl/sql Developer 虛擬機環境&#xff1a;win10&#xff0c;oracle 11g 1.本機和虛擬機互相ping都可以ping通。 2.虛擬機監聽程序已啟動。 二、配置文件 1.本機 tnsname.ora 配置文件 local (DESCRIPTION   (ADDRESS_LIST   (ADDR…

Java消息中間件

1.概述 中間件 非底層操作系統軟件&#xff0c;非業務應用軟件&#xff0c;不是直接給最終用戶使用的&#xff0c;不能直接給客戶帶來價值的軟件統稱為中間件。 消息中間件 管制關注于數據的發送和接收&#xff0c;利用高效可靠的異步消息傳遞機制集成分布式系統。 優點 ① 解…

form 源碼刨析

def clean_name(self) value self.cleaned_data.get(name) if "金-瓶-梅" not in value: raise ValidationError("不符合要求") return value 重寫clean方法 轉載于:https://www.cnblogs.com/wuheng-123/p/9623289.html

兩道面試題

fi [] for i in range(3):def foo(x):print(x i) #由于函數在這時還沒有執行&#xff0c;在這里的i&#xff0c;指向的還是同一個IP地址&#xff0c;所以都是2.fi.appent(foo) for f in fi:f(2)答案&#xff1a;4&#xff0c;4&#xff0c;4 a [0, 1, 2, 3, 4] print(a[-6:6…

uiautomator2進階

點擊控件的具體位置 d(text"Settings").click(offset(0.5, 0.5)) 點擊控件的中間位置 d(text"Settings").click(offset(0, 0)) 點擊控件的左上方 d(text"Settings").click(offset(1, 1)) 點擊控件的右下方 拖動控件 d(text"Settings&quo…

LIS路徑記錄(UVA481)

出自一次很失敗的開學測試 LIS自然會做 可以參見&#xff1a;https://blog.csdn.net/Radium_1209/article/details/79704234 由于對于LIS的nlogn算法不熟悉&#xff0c;導致錯誤理解&#xff0c;記錄的路徑出現了問題&#xff0c;其中還用了n^2的算法記錄路徑&#xff08;好理解…

Activemq源碼、編譯、導入idea、源碼調試總結

1、在本地下載源碼 在GitHub官網搜activemq&#xff0c;找到排名第一的&#xff0c;并打開&#xff0c;如圖所示&#xff0c;拷貝url地址。 activemq托管地址&#xff1a;https://github.com/apache/activemq.git 切換到git bash下&#xff0c;輸入命令&#xff1a; mkdir a…

activiti 視圖

1. application.properties增加如下配置 spring.activiti.database-schema-updatefalsespring.activiti.db-history-usedfalsespring.activiti.db-identity-usedfalse 2. 視圖sql -- 修改表名稱 ALTER TABLE act_id_user RENAME act_id_user_bak1; ALTER TABLE act_id_group RE…

ActiveMQ源碼解析 建立連接

作為一個消息中間件&#xff0c;有客戶端和服務端兩部分代碼&#xff0c;這次的源碼解析系列主要從客戶端的代碼入手&#xff0c;分成建立連接、消息發送、消息消費三個部分。趁著我昨天弄明白了源碼編譯的興奮勁頭還沒過去&#xff0c;今天研究一下建立連接的部分。 如果讀起…

原生Js_實現廣告彈窗

廣告樣式當頁面加載后5s刷新在右下角 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>Gary圖片輪播</title><style type"text/css">#ad{width:300px;height: 300px;background-color:antiquewhite…

springcloud注冊中心eureka

1、前提 springcloud的注冊中心是以springboot為基礎搭建起來的。 開發工具&#xff1a;IDEA 項目管理工具&#xff1a;maven 2、搭建步驟 創建一個web項目&#xff08;建議使用IDEA工具構建項目&#xff09;修改pom文件 <dependency><groupId>org.springframework…

Nancy in .Net Core學習筆記 - 視圖引擎

前文中我們介紹了Nancy中的路由&#xff0c;這一篇我們來介紹一下Nancy中的視圖引擎。 Nancy中如何返回一個視圖(View) 在ASP.NET Mvc中&#xff0c;我們使用ViewResult類來返回一個視圖。Nancy中也提供了類似的功能, 在NancyModule類中&#xff0c;Nancy提供了一個ViewRendere…

設計模式之組合模式(Composite 模式)

引入composite模式 在計算機文件系統中&#xff0c;有文件夾的概念&#xff0c;文件夾里面既可以放入文件也可以放入文件夾&#xff0c;但是文件中卻不能放入任何東西。文件夾和文件構成了一種遞歸結構和容器結構。 雖然文件夾和文件是不同的對象&#xff0c;但是他們都可以被放…

Ansible批量在遠程主機執行命令

Ansible直接執行遠程命令&#xff0c;不用ssh登陸交互執行。    如下&#xff1a;    ansible all -i 192.168.199.180, -m shell -a "ifconfig" -u supermap    參數解釋&#xff1a;    -i 連接到遠程主機“192.168.199.180&#xff0c;”&#xf…

HOJ 2651

一道二分的題目&#xff0c;但要注意不能用double&#xff0c; 并且要注意一下二分的步驟 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define pi 3.1415926535898 #define eps 0.0001 using namespace std; inl…

HierarchicalBeanFactory接口

HierarchicalBeanFactory 提供父容器的訪問功能.至于父容器的設置,需要找ConfigurableBeanFactory的setParentBeanFactory(接口把設置跟獲取給拆開了!). HierarchicalBeanFactory源碼具體&#xff1a; 1、第一個方法返回本Bean工廠的父工廠。這個方法實現了工廠的分層。 2、第…

C++: C++函數聲明的時候后面加const

C: C函數聲明的時候后面加const 轉自&#xff1a;http://blog.csdn.net/zhangss415/article/details/7998123 非靜態成員函數后面加const&#xff08;加到非成員函數或靜態成員后面會產生編譯錯誤&#xff09;&#xff0c;表示成員函數隱含傳入的this指針為const指針&#xff0…