Spring 啓動時讀取應用程序提供的Bean配置信息,並在Spring容器中生成一份相應的Bean配置註冊表,而後根據這張註冊表實例化Bean,裝配好Bean之間的依賴關係,爲上層應用提供準備就緒的運行環境。前端
Bean緩存池:HashMap實現java
Spring 經過一個配置文件描述 Bean 及 Bean 之間的依賴關係,利用 Java 語言的反射功能實例化 Bean 並創建 Bean 之間的依賴關係。 Spring 的 IoC 容器在完成這些底層工做的基礎上,還提供了 Bean 實例緩存、生命週期管理、 Bean 實例代理、事件發佈、資源裝載等高級服務。web
BeanFactory 是 Spring 框架的基礎設施,面向 Spring 自己;spring
ApplicationContext 面向使用 Spring 框架的開發者,幾乎全部的應用場合咱們都直接使用 ApplicationContext 而非底層的 BeanFactory。編程
BeanFactory體系架構:緩存
BeanDefinitionRegistry: Spring 配置文件中每個節點元素在 Spring 容器裏都經過一個 BeanDefinition 對象表示,它描述了 Bean 的配置信息。而 BeanDefinitionRegistry 接口提供了向容器手工註冊 BeanDefinition 對象的方法。tomcat
BeanFactory 接口位於類結構樹的頂端 ,它最主要的方法就是 getBean(String beanName),該方法從容器中返回特定名稱的 Bean,BeanFactory 的功能經過其餘的接口獲得不斷擴展:架構
ListableBeanFactory:該接口定義了訪問容器中 Bean 基本信息的若干方法,如查看Bean 的個數、獲取某一類型 Bean 的配置名、查看容器中是否包括某一 Bean 等方法;mvc
HierarchicalBeanFactory:父子級聯 IoC 容器的接口,子容器能夠經過接口方法訪問父容器; 經過 HierarchicalBeanFactory 接口, Spring 的 IoC 容器能夠創建父子層級關聯的容器體系,子容器能夠訪問父容器中的 Bean,但父容器不能訪問子容器的 Bean。Spring 使用父子容器實現了不少功能,好比在 Spring MVC 中,展示層 Bean 位於一個子容器中,而業務層和持久層的 Bean 位於父容器中。這樣,展示層 Bean 就能夠引用業務層和持久層的 Bean,而業務層和持久層的 Bean 則看不到展示層的 Bean。app
ConfigurableBeanFactory:是一個重要的接口,加強了 IoC 容器的可定製性,它定義了設置類裝載器、屬性編輯器、容器初始化後置處理器等方法;
AutowireCapableBeanFactory:定義了將容器中的 Bean 按某種規則(如按名字匹配、按類型匹配等)進行自動裝配的方法;
SingletonBeanRegistry:定義了容許在運行期間向容器註冊單實例 Bean 的方法;
例子:
使用 Spring 配置文件爲 Car 提供配置信息:beans.xml:
經過 BeanFactory 裝載配置文件,啓動 Spring IoC 容器:
XmlBeanFactory 經過 Resource 裝載 Spring 配置信息並啓動 IoC 容器,而後就能夠經過 BeanFactory#getBean(beanName)方法從 IoC 容器中獲取 Bean 了。經過 BeanFactory 啓動IoC 容器時,並不會初始化配置文件中定義的 Bean,初始化動做發生在第一個調用時。
對於單實例( singleton)的 Bean 來講,BeanFactory會緩存 Bean 實例,因此第二次使用 getBean() 獲取 Bean 時將直接從 IoC 容器的緩存中獲取 Bean 實例。Spring 在 DefaultSingletonBeanRegistry 類中提供了一個用於緩存單實例 Bean 的緩存器,它是一個用HashMap 實現的緩存器,單實例的 Bean 以 beanName 爲鍵保存在這個HashMap 中。
值得一提的是,在初始化 BeanFactory 時,必須爲其提供一種日誌框架,好比使用Log4J, 即在類路徑下提供 Log4J 配置文件,這樣啓動 Spring 容器纔不會報錯。
ApplicationContext 由 BeanFactory 派生而來,提供了更多面向實際應用的功能。
在BeanFactory 中,不少功能須要以編程的方式實現,而在 ApplicationContext 中則能夠經過配置的方式實現。
ApplicationContext 繼承了 HierarchicalBeanFactory 和 ListableBeanFactory 接口,在此基礎上,還經過多個其餘的接口擴展了 BeanFactory 的功能:
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()則可關閉應用上下文。這些接口方法爲容器的控制管理帶來了便利,但做爲開發者,咱們並不須要過多關心這些方法。
使用:
若是配置文件放置在類路徑下,用戶能夠優先使用 ClassPathXmlApplicationContext 實現類:
若是配置文件放置在文件系統的路徑下,則能夠優先考慮使用 FileSystemXmlApplicationContext 實現類:
Spring 3.0 支持基於類註解的配置方式,主要功能來自於 Spring 的一個名爲 JavaConfig 子項目,目前 JavaConfig已經升級爲 Spring核心框架的一部分。
ApplicationContext 在初始化應用上下文時就實例化全部單實例的 Bean。
WebApplication體系架構:
WebApplicationContext 是專門爲 Web 應用準備的,它容許從相對於 Web 根目錄的路徑中裝載配置文件完成初始化工做。從WebApplicationContext 中能夠得到 ServletContext 的引用,整個 Web 應用上下文對象將做爲屬性放置到 ServletContext 中,以便 Web 應用環境能夠訪問 Spring 應用上下文。 WebApplicationContext 定義了一個常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,在上下文啓動時, WebApplicationContext 實例即以此爲鍵放置在 ServletContext 的屬性列表中,所以咱們能夠直接經過如下語句從 Web 容器中獲取WebApplicationContext:
Spring 和 Web 應用的上下文融合:
WebApplicationContext 的初始化方式:WebApplicationContext 須要 ServletContext 實例,它必須在擁有 Web 容器的前提下才能完成啓動的工做。能夠在 web.xml 中配置自啓動的 Servlet 或定義 Web 容器監聽器( ServletContextListener),藉助這二者中的任何一個就能夠完成啓動 Spring Web 應用上下文的工做。Spring 分別提供了用於啓動 WebApplicationContext 的 Servlet 和 Web 容器監聽器:
org.springframework.web.context.ContextLoaderServlet;
org.springframework.web.context.ContextLoaderListener
因爲 WebApplicationContext 須要使用日誌功能,好比日誌框架使用Log4J,用戶能夠將 Log4J 的配置文件放置到類路徑 WEB-INF/classes 下,這時 Log4J 引擎便可順利啓動。若是 Log4J 配置文件放置在其餘位置,用戶還必須在 web.xml 指定 Log4J 配置文件位置。
1、當調用者經過 getBean(beanName)向容器請求某一個 Bean 時,若是容器註冊了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor 接口,在實例化 Bean 以前,將調用接口的 postProcessBeforeInstantiation()方法;
2、根據配置狀況調用 Bean 構造函數或工廠方法實例化 Bean;
3、若是容器註冊了 InstantiationAwareBeanPostProcessor 接口,在實例化 Bean 以後,調用該接口的 postProcessAfterInstantiation()方法,可在這裏對已經實例化的對象進行一些「梳妝打扮」;
4、若是 Bean 配置了屬性信息,容器在這一步着手將配置值設置到 Bean 對應的屬性中,不過在設置每一個屬性以前將先調用InstantiationAwareBeanPostProcessor 接口的postProcessPropertyValues()方法;
5、調用 Bean 的屬性設置方法設置屬性值;
6、若是 Bean 實現了 org.springframework.beans.factory.BeanNameAware 接口,將調用setBeanName()接口方法,將配置文件中該 Bean 對應的名稱設置到 Bean 中;
7、若是 Bean 實現了 org.springframework.beans.factory.BeanFactoryAware 接口,將調用 setBeanFactory()接口方法,將 BeanFactory 容器實例設置到 Bean 中;
8、若是 BeanFactory 裝配了 org.springframework.beans.factory.config.BeanPostProcessor後處理器,將調用 BeanPostProcessor 的 Object postProcessBeforeInitialization(Object bean, String beanName)接口方法對 Bean 進行加工操做。其中入參 bean 是當前正在處理的 Bean,而 beanName 是當前 Bean 的配置名,返回的對象爲加工處理後的 Bean。用戶可使用該方法對某些 Bean 進行特殊的處理,甚至改變 Bean 的行爲, BeanPostProcessor 在 Spring 框架中佔有重要的地位,爲容器提供對 Bean 進行後續加工處理的切入點, Spring 容器所提供的各類「神奇功能」(如 AOP,動態代理等)都經過 BeanPostProcessor 實施;
9、若是 Bean 實現了 InitializingBean 的接口,將調用接口的 afterPropertiesSet()方法;
10、若是在經過 init-method 屬性定義了初始化方法,將執行這個方法;
11、BeanPostProcessor 後處理器定義了兩個方法:其一是 postProcessBeforeInitialization() 在第 8 步調用;其二是 Object postProcessAfterInitialization(Object bean, String beanName)方法,這個方法在此時調用,容器再次得到對 Bean 進行加工處理的機會;
12、若是在中指定 Bean 的做用範圍爲 scope=「prototype」,將 Bean 返回給調用者,調用者負責 Bean 後續生命的管理, Spring 再也不管理這個 Bean 的生命週期。若是做用範圍設置爲 scope=「singleton」,則將 Bean 放入到 Spring IoC 容器的緩存池中,並將 Bean引用返回給調用者, Spring 繼續對這些 Bean 進行後續的生命管理;
13、對於 scope=「singleton」的 Bean,當容器關閉時,將觸發 Spring 對 Bean 的後續生命週期的管理工做,首先若是 Bean 實現了 DisposableBean 接口,則將調用接口的afterPropertiesSet()方法,能夠在此編寫釋放資源、記錄日誌等操做;
14、對於 scope=「singleton」的 Bean,若是經過的 destroy-method 屬性指定了 Bean 的銷燬方法, Spring 將執行 Bean 的這個方法,完成 Bean 資源的釋放等操做。
能夠將這些方法大體劃分爲三類:
Bean 自身的方法:如調用 Bean 構造函數實例化 Bean,調用 Setter 設置 Bean 的屬性值以及經過的 init-method 和 destroy-method 所指定的方法;
Bean 級生命週期接口方法:如 BeanNameAware、 BeanFactoryAware、 InitializingBean 和 DisposableBean,這些接口方法由 Bean 類直接實現;
容器級生命週期接口方法:在上圖中帶「★」 的步驟是由 InstantiationAwareBean PostProcessor 和BeanPostProcessor 這兩個接口實現,通常稱它們的實現類爲「 後處理器」 。 後處理器接口通常不禁 Bean 自己實現,它們獨立於 Bean,實現類以容器附加裝置的形式註冊到 Spring 容器中並經過接口反射爲 Spring 容器預先識別。當Spring 容器建立任何 Bean 的時候,這些後處理器都會發生做用,因此這些後處理器的影響是全局性的。固然,用戶能夠經過合理地編寫後處理器,讓其僅對感興趣Bean 進行加工處理
ApplicationContext 和 BeanFactory 另外一個最大的不一樣之處在於:ApplicationContext會利用 Java 反射機制自動識別出配置文件中定義的 BeanPostProcessor、 InstantiationAwareBeanPostProcessor 和 BeanFactoryPostProcessor,並自動將它們註冊到應用上下文中;然後者須要在代碼中經過手工調用 addBeanPostProcessor()方法進行註冊。這也是爲何在應用開發時,咱們廣泛使用 ApplicationContext 而不多使用 BeanFactory 的緣由之一
容器啓動過程
web環境下Spring容器、SpringMVC容器啓動過程:
首先,對於一個web應用,其部署在web容器中,web容器提供其一個全局的上下文環境,這個上下文就是ServletContext,其爲後面的spring IoC容器提供宿主環境;
其次,在web.xml中會提供有contextLoaderListener(或ContextLoaderServlet)。在web容器啓動時,會觸發容器初始化事件,此時contextLoaderListener會監聽到這個事件,其contextInitialized方法會被調用,在這個方法中,spring會初始化一個啓動上下文,這個上下文被稱爲根上下文,即WebApplicationContext,這是一個接口類,確切的說,其實際的實現類是XmlWebApplicationContext。這個就是spring的IoC容器,其對應的Bean定義的配置由web.xml中的context-param標籤指定。在這個IoC容器初始化完畢後,spring容器以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE爲屬性Key,將其存儲到ServletContext中,便於獲取;
再次,contextLoaderListener監聽器初始化完畢後,開始初始化web.xml中配置的Servlet,這個servlet能夠配置多個,以最多見的DispatcherServlet爲例(Spring MVC),這個servlet其實是一個標準的前端控制器,用以轉發、匹配、處理每一個servlet請求。DispatcherServlet上下文在初始化的時候會創建本身的IoC上下文容器,用以持有spring mvc相關的bean,這個servlet本身持有的上下文默認實現類也是XmlWebApplicationContext。在創建DispatcherServlet本身的IoC上下文時,會利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先從ServletContext中獲取以前的根上下文(即WebApplicationContext)做爲本身上下文的parent上下文(即第2步中初始化的XmlWebApplicationContext做爲本身的父容器)。有了這個parent上下文以後,再初始化本身持有的上下文(這個DispatcherServlet初始化本身上下文的工做在其initStrategies方法中能夠看到,大概的工做就是初始化處理器映射、視圖解析等)。初始化完畢後,spring以與servlet的名字相關(此處不是簡單的以servlet名爲Key,而是經過一些轉換)的屬性爲屬性Key,也將其存到ServletContext中,以便後續使用。這樣每一個servlet就持有本身的上下文,即擁有本身獨立的bean空間,同時各個servlet共享相同的bean,即根上下文定義的那些bean。
Bean加載過程
Spring的高明之處在於,它使用衆多接口描繪出了全部裝置的藍圖,構建好Spring的骨架,繼而經過繼承體系層層推演,不斷豐富,最終讓Spring成爲有血有肉的完整的框架。因此查看Spring框架的源碼時,有兩條清晰可見的脈絡:
接口層描述了容器的重要組件及組件間的協做關係;
繼承體系逐步實現組件的各項功能。
接口層清晰地勾勒出Spring框架的高層功能,框架脈絡呼之欲出。有了接口層抽象的描述後,不但Spring本身能夠提供具體的實現,任何第三方組織也能夠提供不一樣實現, 能夠說Spring完善的接口層使框架的擴展性獲得了很好的保證。縱向繼承體系的逐步擴展,分步驟地實現框架的功能,這種實現方案保證了框架功能不會堆積在某些類的身上,形成太重的代碼邏輯負載,框架的複雜度被完美地分解開了。
Spring組件按其所承擔的角色能夠劃分爲兩類:
1)物料組件:Resource、BeanDefinition、PropertyEditor以及最終的Bean等,它們是加工流程中被加工、被消費的組件,就像流水線上被加工的物料;
BeanDefinition:Spring經過BeanDefinition將配置文件中的配置信息轉換爲容器的內部表示,並將這些BeanDefinition註冊到BeanDefinitionRegistry中。Spring容器的後續操做直接從BeanDefinitionRegistry中讀取配置信息。
2)加工設備組件:ResourceLoader、BeanDefinitionReader、BeanFactoryPostProcessor、InstantiationStrategy以及BeanWrapper等組件像是流水線上不一樣環節的加工設備,對物料組件進行加工處理。
InstantiationStrategy:負責實例化Bean操做,至關於Java語言中new的功能,並不會參與Bean屬性的配置工做。屬性填充工做留待BeanWrapper完成
BeanWrapper:繼承了PropertyAccessor和PropertyEditorRegistry接口,BeanWrapperImpl內部封裝了兩類組件:
(1)Bean包裹器 (2)屬性訪問器 (3)屬性編輯器註冊表。
PropertyAccessor:定義了各類訪問Bean屬性的方法。PropertyEditorRegistry:屬性編輯器的註冊表
該圖描述了Spring容器從加載配置文件到建立出一個完整Bean的做業流程:
1、ResourceLoader從存儲介質中加載Spring配置信息,並使用Resource表示這個配置文件的資源;
2、BeanDefinitionReader讀取Resource所指向的配置文件資源,而後解析配置文件。配置文件中每個解析成一個BeanDefinition對象,並保存到BeanDefinitionRegistry中;
3、容器掃描BeanDefinitionRegistry中的BeanDefinition,使用Java的反射機制自動識別出Bean工廠後處理後器(實現BeanFactoryPostProcessor接口)的Bean,而後調用這些Bean工廠後處理器對BeanDefinitionRegistry中的BeanDefinition進行加工處理。主要完成如下兩項工做:
4、Spring容器從BeanDefinitionRegistry中取出加工後的BeanDefinition,並調用InstantiationStrategy着手進行Bean實例化的工做;
5、在實例化Bean時,Spring容器使用BeanWrapper對Bean進行封裝,BeanWrapper提供了不少以Java反射機制操做Bean的方法,它將結合該Bean的BeanDefinition以及容器中屬性編輯器,完成Bean屬性的設置工做;
6、利用容器中註冊的Bean後處理器(實現BeanPostProcessor接口的Bean)對已經完成屬性設置工做的Bean進行後續加工,直接裝配出一個準備就緒的Bean。
Spring IOC容器主要有繼承體系底層的BeanFactory、高層的ApplicationContext和WebApplicationContext
Bean有本身的生命週期
容器啓動原理:Spring應用的IOC容器經過tomcat的Servlet或Listener監聽啓動加載;Spring MVC的容器由DispatchServlet做爲入口加載;Spring容器是Spring MVC容器的父容器
容器加載Bean原理:
BeanDefinitionReader讀取Resource所指向的配置文件資源,而後解析配置文件。配置文件中每個解析成一個BeanDefinition對象,並保存到BeanDefinitionRegistry中;
容器掃描BeanDefinitionRegistry中的BeanDefinition;調用InstantiationStrategy進行Bean實例化的工做;使用BeanWrapper完成Bean屬性的設置工做;
單例Bean緩存池:Spring 在 DefaultSingletonBeanRegistry 類中提供了一個用於緩存單實例 Bean 的緩存器,它是一個用 HashMap 實現的緩存器,單實例的 Bean 以 beanName 爲鍵保存在這個HashMap 中。
歡迎你們關注我新開通的公衆號【風平浪靜如碼】,海量Java相關文章,學習資料都會在裏面更新,整理的資料也會放在裏面。
以爲寫的還不錯的就點個贊,加個關注唄!點關注,不迷路,持續更新!!!