面試之敵系列 5 Spring

SpringMVC 流程

image.png
image.png
image.png
image.png

  1. 請求轉發給到DispatcherServlet。
  2. DispatcherServlet請求HandleMapping,查找到對應的handle。能夠根據註解或xml 文件查找。
  3. 找到對應的handle 以後,會加入一些必要的和配置的攔截器,組成了一個HandleExcutionChain對象返回DispatcherServlet
  4. DispatcherServlet根據其中的handle,請求對應的HandleAdapter去執行這個handle。並返回一個ModelAndView 對象給到DispatcherServlet。
  5. DispatcherServlet請求ViewResolver解析這個ModelAndView對象,獲得一個View 對象,這個對象比較容易轉換成其餘的視圖技術【】
  6. 根據獲得的view 進行視圖的渲染,返回響應給請求。完成這次請求流程。

執行鏈的執行是按照配置文件的前後順序執行的。當執行到handle的時候,會去請求合適的HandleAdapter去執行,而不是本身執行。執行以後返回一個ModelAndView對象。以後在請求一個ViewResolver來解析ModelAndView對象獲得View對象,這個對象能夠用於解析和渲染獲得最終的數據。html

Spring 的總體的架構

image.png

Core Container

ore Container(核心容器)包含Core、Beans、Context和Expression Language模塊。Core和Beans模塊是框架的基礎部分,提供IoC(控制反轉)和依賴注入特性。此處涉及到的基礎概念是BeanFactory,其提供對於Factory模式的經典實現來消除對程序性單例模式的須要,並真正的容許從程序邏輯中分離出依賴關係和配置。程序員

  1. Core模塊主要包含框架基本的核心工具類,Spring的其餘組件都要使用這裏的類,是其餘組件的基本核心。
  2. Bean模塊是全部應用都要使用到的,包含訪問配置文件、建立和管理bean以及進行Inversion of Control/Dependency Injection(IoC/DI)操做相關的全部類。
  3. Context模塊構建於Core和Bean模塊基礎之上,其提供了一種相似於JNDI註冊器的框架式的對象訪問方法。其繼承了Bean的特性,爲Spring核心提供了大量擴展,添加了對國際化(例如資源綁定)、事件傳播、資源加載和對Context的透明建立的支持。同時,也支持J2EE的一些特性,例如EJB、JMX和基礎的遠程處理。ApplicationContext接口是該模塊的關鍵。
  4. Expression Language模塊提供了一個強大的表達式語言用於在運行時查詢和操做對象。該語言支持設置/獲取屬性的值,屬性的分配,方法的調用,訪問數組上下文(accessiong the context of arrays)、容器和索引器、邏輯和算術運算符、命名變量以及從Spring的IoC容器中根據名稱檢索對象。同時,也支持list投影、選擇和通常的list集合。

Data Access/Integration

Data Access/Integration包含JDBC、ORM、OXM、JMX和Transaction模塊。web

  1. JDBC模塊提供一個JDBC抽象層,能夠徹底消除冗長的JDBC編碼和解析數據庫廠商特有的錯誤代碼。此模塊包含了Spring對JDBC數據訪問進行封裝的全部類。面試

  2. ORM模塊爲流行的對象-關係映射API,如JPA、JDO、Hibernate、iBatis等,提供了一個交互層。利用ORM封裝包,能夠混合使用全部Spring提供的特性進行O/R映射。spring

  3. OXM模塊提供了一個對Object/XML映射實現的抽象層,Object/XML映射實現包括JAXB、Castor、XMLBeans、JiBX和XStream。數據庫

  4. JMS(Java Messaging Service)包含了一些製造和消息消息的特性。編程

  5. Transaction模塊支持編程和聲明性的事物管理,這些事物必須實現特定的接口,而且對全部的POJO適用。數組

Web

Web上下文模塊創建在應用程序上下文模塊之上,爲基於Web的應用程序提供上下文。同時,該模塊還簡化了處理多部分請求以及將請求參數綁定到域對象的工做。緩存

  1. Web模塊提供了基礎的面向Web的集成特性。例如,多文件上傳、使用servlet listeners初始化IoC容器以及一個面向Web的應用上下文,還包含了Spring遠程支持中Web的相關部分。服務器

  2. Web-Servlet模塊(web.servlet.jar)包含了Spring的model-view-controller(MVC)實現。Spring的MVC框架使得模型範圍內的代碼和web forms之間可以清楚地分離開來,並與Spring框架的其餘特性集成在一塊兒。

  3. Web-Struts模塊提供了對Struts的支持,使得類在Spring應用中可以與一個典型的Struts Web層集成在一塊兒。(PS:此支持在Spring3.0中是deprecated的)。

  4. Web-Portlet模塊提供了用於Portlet環境和Web-Servlet模塊的MVC的實現。

AOP

AOP模塊提供了一個符合AOP聯盟標準的面向切面編程的實現,能夠定義例如方法攔截器和切點,從而將邏輯代碼分開,下降耦合性。利用source-level的元數據功能,還能夠將各類行爲信息合併到代碼中。經過配置管理特性,該模塊直接將面向切面的編程集成到框架中,進而是框架管理的對象支持AOP。同時,該模塊爲基於Spring的應用程序中的對象提供了事務管理服務。經過使用Spring AOP,不用依賴EJB組件,就能夠將聲明性事務管理集成到應用程序中。

  1. Aspects提供了對AspectJ的集成支持。

  2. Instrumentation提供了class instrumentation支持和classloader實現,進而能夠在特定的應用服務器上使用。

BeanFactory 和 ApplicationContext 有什麼區別

beanfactory and applicationcontext的區別

IoC: 控制反轉,是一種思想,表示將由程序員自動建立對象的權限交由spring框架來管理。當使用spring 的時候,對象的建立和其生命週期的維護全權交由spring來作,咱們只要給出依賴的配置便可。

Spring IoC容器

Spring IoC容器的設計主要基於下面的兩個主要的接口

  1. BeanFactory
  2. ApplicationContext

其中,ApplicationContext是BeanFactory的派生接口,可是它實現了更多,更全面的功能,也就是一種更加高級的接口,最經常使用的一個實現類就是ClassPathXMLApplicationContex類。所以,在絕大部分的時候使用ApplicationContext。

image.png

區別

  1. BeanFactory: 是Spring中最底層的接口,只提供了最簡單的IoC功能,負責配置,建立和管理bean。在應用中,通常不使用 BeanFactory,而推薦使用ApplicationContext

  2. ApplicationContext:

    1. 繼承了 BeanFactory,擁有了基本的 IoC 功能;
    2. 除此以外,ApplicationContext 還提供瞭如下功能:
      • ① 支持國際化;
      • ② 支持消息機制;
      • ③ 支持統一的資源加載;
      • ④ 支持AOP功能;

Spring IoC 的初始化和依賴注入

雖然 Spring IoC 容器的生成十分的複雜,可是大致瞭解一下 Spring IoC 初始化的過程仍是必要的。這對於理解 Spring 的一系列行爲是頗有幫助的。

使用任何一個類,都須要先定義,聲明,初始化這三個步驟。

  1. Resource 定位 Spring IoC 容器先根據開發者的配置,進行資源的定位,在 Spring 的開發中,經過 XML 或者註解都是十分常見的方式,定位的內容是由開發者提供的。

  2. BeanDefinition 的載入 這個時候只是將 Resource 定位到的信息,保存到 Bean 定義(BeanDefinition)中,此時並不會建立 Bean 的實例

  3. BeanDefinition 的註冊 這個過程就是將 BeanDefinition 的信息發佈到 Spring IoC 容器中 注意:此時仍然沒有對應的 Bean 的實例。

Ioc實現

最後咱們簡單說說IoC是如何實現的。想象一下若是咱們本身來實現這個依賴注入的功能,咱們怎麼來作? 無外乎:

讀取標註或者配置文件,看看JuiceMaker依賴的是哪一個Source,拿到類名 使用反射的API,基於類名實例化對應的對象實例 將對象實例,經過構造函數或者 setter,傳遞給 JuiceMaker

區別

  1. 若是使用ApplicationContext,若是配置的bean是singleton,那麼無論你有沒有或想不想用它,它都會被實例化。好處是能夠預先加載,壞處是浪費內存。

  2. BeanFactory,當使用BeanFactory實例化對象時,配置的bean不會立刻被實例化,而是等到你使用該bean的時候(getBean)纔會被實例化。好處是節約內存,壞處是速度比較慢。多用於移動設備的開發。

  3. 沒有特殊要求的狀況下,應該使用ApplicationContext完成。由於BeanFactory能完成的事情,ApplicationContext都能完成,而且提供了更多接近如今開發的功能。

生命週期

image.png

Spring上下文中的Bean也相似,【Spring上下文的生命週期】

  1. 實例化一個Bean,也就是咱們一般說的new
  2. 按照Spring上下文對實例化的Bean進行配置,也就是IOC注入
  3. 若是這個Bean實現了BeanNameAware接口,會調用它實現的setBeanName(String beanId)方法,此處傳遞的是Spring配置文件中Bean的ID
  4. 若是這個Bean實現了BeanFactoryAware接口,會調用它實現的setBeanFactory(),傳遞的是Spring工廠自己(能夠用這個方法獲取到其餘Bean)
  5. 若是這個Bean實現了ApplicationContextAware接口,會調用setApplicationContext(ApplicationContext)方法,傳入Spring上下文,該方式一樣能夠實現步驟4,但比4更好,覺得ApplicationContext是BeanFactory的子接口,有更多的實現方法
  6. 若是這個Bean關聯了BeanPostProcessor接口,將會調用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor常常被用做是Bean內容的更改,而且因爲這個是在Bean初始化結束時調用After方法,也可用於內存或緩存技術
  7. 若是這個Bean在Spring配置文件中配置了init-method屬性會自動調用其配置的初始化方法
  8. 若是這個Bean關聯了BeanPostProcessor接口,將會調用postAfterInitialization(Object obj, String s)方法注意:以上工做完成之後就能夠用這個Bean了,那這個Bean是一個single的,因此通常狀況下咱們調用同一個ID的Bean會是在內容地址相同的實例
  9. 當Bean再也不須要時,會通過清理階段,若是Bean實現了DisposableBean接口,會調用其實現的destroy方法
  10. 最後,若是這個Bean的Spring配置中配置了destroy-method屬性,會自動調用其配置的銷燬方法以上10步驟能夠做爲面試或者筆試的模板,另外咱們這裏描述的是應用Spring上下文Bean的生命週期,若是應用Spring的工廠也就是BeanFactory的話去掉第5步就Ok了

其實在初始化以前的不少東西很像是一個聲明,註冊的一個過程,使得spring 能夠對此bean進行必要的控制的過程。

Bean的建立流程

image.png

spring讀取配置或註解的過程

1:先經過掃描指定包路徑下的spring註解,好比@Component、@Service、@Lazy @Sope等spring識別的註解或者是xml配置的屬性(經過讀取流,解析成Document,Document)而後spring會解析這些屬性,將這些屬性封裝到BeanDefintaion這個接口的實現類中.

好比這個配置Bean,spring也會將className、scope、lazy等這些屬性裝配到PersonAction對應的BeanDefintaion中.具體採用的是BeanDefinitionParser接口中的parse(Element element, ParserContext parserContext)方法,該接口有不少不一樣的實現類。經過實現類去解析註解或者xml而後放到BeanDefination中,BeanDefintaion的做用是集成了咱們的配置對象中的各類屬性,重要的有這個bean的ClassName,還有是不是Singleton、對象的屬性和值等(若是是單例的話,後面會將這個單例對象放入到spring的單例池中)。spring後期若是須要這些屬性就會直接從它中獲取。而後,再註冊到一個ConcurrentHashMap中,在spring中具體的方法就是registerBeanDefinition(),這個Map存的key是對象的名字,好比Person這個對象,它的名字就是person,值是BeanDefination,它位於DefaultListableBeanFactory類下面的beanDefinitionMap類屬性中,同時將全部的bean的名字放入到beanDefinitionNames這個list中,目的就是方便取beanName;

spring的bean的生命週期

spring的bean生命週期其實最核心的分爲4個步驟,只要理清三個關鍵的步驟,其餘的只是在這三個細節中添加不一樣的細節實現,也就是spring的bean生明週期:

實例化和初始化的區別:實例化是在jvm的堆中建立了這個對象實例,此時它只是一個空的對象,全部的屬性爲null。而初始化的過程就是講對象依賴的一些屬性進行賦值以後,調用某些方法來開啓一些默認加載。好比spring中配置的數據庫屬性Bean,在初始化的時候就會將這些屬性填充,好比driver、jdbcurl等,而後初始化鏈接

image.png

2.1:實例化 Instantiation

AbstractAutowireCapableBeanFactory.doCreateBean中會調用createBeanInstance()方法,該階段主要是從beanDefinitionMap循環讀取bean,獲取它的屬性,而後利用反射(core包下有ReflectionUtil會先強行將構造方法setAccessible(true))讀取對象的構造方法(spring會自動判斷是不是有參數仍是無參數,以及構造方法中的參數是否可用),而後再去建立實例(newInstance)
複製代碼

2.2:初始化

初始化主要包括兩個步驟,一個是屬性填充,另外一個就是具體的初始化過程

2.2.1:屬性賦值
PopulateBean()會對bean的依賴屬性進行填充,@AutoWired註解注入的屬性就發生這個階段,假如咱們的bean有不少依賴的對象,那麼spring會依次調用這些依賴的對象進行實例化,注意這裏可能會有循環依賴的問題。後面咱們會講到spring是如何解決循環依賴的問題
複製代碼
2.2.2:初始化 Initialization
初始化的過程包括將初始化好的bean放入到spring的緩存中、填充咱們預設的屬性進一步作後置處理等
複製代碼

2.3: 使用和銷燬 Destruction

在Spring將全部的bean都初始化好以後,咱們的業務系統就能夠調用了。而銷燬主要的操做是銷燬bean,主要是伴隨着spring容器的關閉,此時會將spring的bean移除容器之中。此後spring的生命週期到這一步完全結束,再也不接受spring的管理和約束。
複製代碼

spring 循環依賴

  1. bean有一個建立中的狀態
  2. bean 有一個提早曝光的機制(所謂的提早曝光,就是僅僅實例化,沒有初始化完成的)。這裏考慮的是一個bean建立的完整流程是會包括實例化和初始化的。提早曝光就是僅僅實例化尚未初始化。
  3. 通常的依賴循環發生在setter 裏面的單例模式的建立過程當中。

spring單例對象的初始化大略分爲三步:

  • createBeanInstance:實例化,其實也就是調用對象的構造方法實例化對象
  • populateBean:填充屬性,這一步主要是多bean的依賴屬性進行填充
  • initializeBean:調用spring xml中的init 方法。

從上面講述的單例bean初始化步驟咱們能夠知道,循環依賴主要發生在第1、第二步。也就是構造器循環依賴和field循環依賴。 接下來,咱們具體看看spring是如何處理三種循環依賴的。

這三級緩存的做用分別是: singletonFactories : 進入實例化階段的單例對象工廠的cache (三級緩存) earlySingletonObjects :完成實例化可是還沒有初始化的,提早暴光的單例對象的Cache (二級緩存) singletonObjects:完成初始化的單例對象的cache(一級緩存)

經過查詢第三級緩存能夠知道哪些對象其實已經建立,能夠依賴了,儘管此時尚未完成初始化的整個流程。這樣就能夠提早終止了依賴圈。

這樣作有什麼好處呢?讓咱們來分析一下「A的某個field或者setter依賴了B的實例對象,同時B的某個field或者setter依賴了A的實例對象」這種循環依賴的狀況。A首先完成了初始化的第一步,而且將本身提早曝光到singletonFactories中,此時進行初始化的第二步,發現本身依賴對象B,此時就嘗試去get(B),發現B尚未被create,因此走create流程,B在初始化第一步的時候發現本身依賴了對象A,因而嘗試get(A),嘗試一級緩存singletonObjects(確定沒有,由於A還沒初始化徹底),嘗試二級緩存earlySingletonObjects(也沒有),嘗試三級緩存singletonFactories,因爲A經過ObjectFactory將本身提早曝光了,因此B可以經過ObjectFactory.getObject拿到A對象(雖然A尚未初始化徹底,可是總比沒有好呀),B拿到A對象後順利完成了初始化階段一、二、3,徹底初始化以後將本身放入到一級緩存singletonObjects中。此時返回A中,A此時能拿到B的對象順利完成本身的初始化階段二、3,最終A也完成了初始化,進去了一級緩存singletonObjects中,並且更加幸運的是,因爲B拿到了A的對象引用,因此B如今hold住的A對象完成了初始化。

new 和 newinstance的區別

new一個對象過程分析

  1. 全部的類都是在第一次使用時,被動態加載到jvm內存中,即首次建立對象時,或者類中的靜態方法首次被調用時,或者靜態屬性被訪問時,類加載器定位找到對應的class文件;

  2. 類加載器把class文件載入內存,並生成class對象,把對象中全部的靜態資源都執行一遍,並把這些靜態資源存放到jvm的方法區中,有且只在class對象首次生成時執行一次;

  3. new建立對象時,首先檢查該類的class文件是否已加載到jvm內存中並生成class對象,如有,則會在jvm堆內存中爲該類分配足夠的空間;

  4. 把存儲的空間清空,並把該類的全部的基本數據類型設置成默認值,對象引用設置null;

  5. 繼續執行字段中被自定義的值的一些初始化操做;

  6. 調用構造方法,便建立了一個對象。

與反射機制建立對象的區別

  1. new的對象在編譯環境中要必須在類路徑中有,class.forName()在編譯時能夠不在類路徑中,因此class.forName()指定了ClassLoader後,就能夠在指定的環境中查找某些類,即:new一個對象容許class文件還沒加載進來,jvm虛擬機會自動檢查內存中是否有這個class對象,若沒有就經過類加載器加載進來,而newInstance()必需要確保class文件已經加載進內存中才能產生一個對象,這時需經過class.foName()方法加載class文件。

  2. newInstance()其實是把new這個方式分解爲兩步,即首先調用Class加載方法加載某個類,而後實例化。

容器實例化流程

dominicpoi.com/2019/06/13/…

huzb.me/2019/03/04/…

bean 實例化流程

相關文章
相關標籤/搜索