當前位置:首頁 » 操作系統 » spring源碼深度解析pdf

spring源碼深度解析pdf

發布時間: 2023-03-07 04:02:55

1. [Spring boot源碼解析] 2 啟動流程分析

在了解 Spring Boot 的啟動流程的時候,我們先看一下一個Spring Boot 應用是如何啟動的,如下是一個簡單的 SpringBoot 程序,非常的簡潔,他是如何做到的呢,我們接下來就將一步步分解。

我們追蹤 SpringApplication.run() 方法,其實最終它主要的邏輯是新建一個 SpringApplication ,然後調用他的 run 方法,如下:

我們先來看一下創建 SpringApplication 的方法:

在將Main class 設置 primarySources 後,調用了 WebApplicationType.deceFromClasspath() 方法,該方法是為了檢查當前的應用類型,並設置給 webApplicationType 。 我們進入 deceFromClasspath 方法 :

這里主要是通過類載入器判斷是否存在 REACTIVE 相關的類信息,假如有就代表是一個 REACTIVE 的應用,假如不是就檢查是否存在 Servelt 和 ,假如都沒有,就代表應用為非 WEB 類應用,返回 NONE ,默認返回 SERVLET 類型,我們這期以我們目前最常使用的 SERVLET 類型進行講解,所以我們在應用中引入了 spring-boot-starter-web 作為依賴:

他會包含 Spring-mvc 的依賴,所以就包含了內嵌 tomcat 中的 Servlet 和 Spring-web 中的 ,因此返回了 SERVLET 類型。

回到剛才創建 SpringApplication 的構建方法中,我們設置完成應用類型後,就尋找所有的 Initializer 實現類,並設置到 SpringApplication 的 Initializers 中,這里先說一下 getSpringFactoriesInstances 方法,我們知道在我們使用 SpringBoot 程序中,會經常在 META-INF/spring.factories 目錄下看到一些 EnableAutoConfiguration ,來出發 config 類注入到容器中,我們知道一般一個 config 類要想被 SpringBoot 掃描到需要使用 @CompnentScan 來掃描具體的路徑,對於 jar 包來說這無疑是非常不方便的,所以 SpringBoot 提供了另外一種方式來實現,就是使用 spring.factories ,比如下面這個,我們從 Springboot-test 中找到的例子,這里先定義了一個ExampleAutoConfiguration,並加上了 Configuration 註解:

然後在 spring.factories 中定義如下:

那這種方式是怎麼實現的你,這就要回到我們剛才的方法 getSpringFactoriesInstances :

我們先來看一下傳入參數,這里需要注意的是 args,這個是初始化對應 type 的時候傳入的構造參數,我們先看一下 SpringFactoriesLoader#loadFactoryNames 方法:

首先是會先檢查緩存,假如緩存中存在就直接返回,假如沒有就調用 classLoader#getResources 方法,傳入 META-INF/spring.factories ,即獲取所有 jar 包下的對應文件,並封裝成 UrlResource ,然後使用 PropertiesLoaderUtils 將這些信息讀取成一個對一對的 properties,我們觀察一下 spring.factories 都是按 properties 格式排版的,假如有多個就用逗號隔開,所以這里還需要將逗號的多個類分隔開來,並加到 result 中,由於 result 是一個 LinkedMultiValueMap 類型,支持多個值插入,最後放回緩存中。最終完成載入 META-INF/spring.factories 中的配置,如下:

我們可以看一下我們找到的 initializer 有多少個:

在獲取到所有的 Initializer 後接下來是調用 方法進行初始化。

這里的 names 就是我們上面通過類載入器載入到的類名,到這里會先通過反射生成 class 對象,然後判斷該類是否繼承與 ApplicationContextInitializer ,最後通過發射的方式獲取這個類的構造方法,並調用該構造方法,傳入已經定義好的構造參數,對於 ApplicationContextInitializer 是無參的構造方法,然後初始化實例並返回,回到原來的方法,這里會先對所有的 ApplicationContextInitializer 進行排序,調用 #sort(instances) 方法,這里就是根據 @Order 中的順序進行排序。

接下來是設置 ApplicationListener ,我們跟進去就會發現這里和上面獲取 ApplicationContextInitializer 的方法如出一轍,最終會載入到如圖的 15 個 listener (這里除了 外,其他都是 SpringBoot 內部的 Listener):

在完成 SpringApplication 對象的初始化後,我們進入了他的 run 方法,這個方法幾乎涵蓋了 SpringBoot 生命周期的所有內容,主要分為九個步驟,每一個步驟這里都使用註解進行標識:

主要步驟如下:
第一步:獲取 SpringApplicationRunListener, 然後調用他的 staring 方法啟動監聽器。
第二步:根據 SpringApplicationRunListeners以及參數來准備環境。
第三步:創建 Spring 容器。
第四步:Spring 容器的前置處理。
第五步:刷新 Spring 容器。
第六步: Spring 容器的後置處理器。
第七步:通知所有 listener 結束啟動。
第八步:調用所有 runner 的 run 方法。
第九步:通知所有 listener running 事件。
我們接下來一一講解這些內容。

我們首先看一下第一步,獲取 SpringApplicationRunListener :

這里和上面獲取 initializer 和 listener 的方式基本一致,都是通過 getSpringFactoriesInstances , 最終只找到一個類就是: org.springframework.boot.context.event.EventPublishingRunListener ,然後調用其構造方法並傳入產生 args , 和 SpringApplication 本身:

我們先看一下構造函數,首先將我們獲取到的 ApplicationListener 集合添加到initialMulticaster 中, 最後都是通過操作 來進行廣播,我,他繼承於 ,我們先看一下他的 addApplicationListener 方法:

我們可以看出,最後是放到了 applicationListenters 這個容器中。他是 defaultRetriever 的成員屬性, defaultRetriever 則是 的私有類,我們簡單看一下這個類:

我們只需要看一下這里的 getApplicationListeners 方法,它主要是到 beanFactory 中檢查是否存在多的 ApplicationListener 和舊的 applicationListeners 組合並返回,接著執行 listener 的 start 方法,最後也是調用了 的 multicastEvent 查找支持對應的 ApplicationEvent 類型的通知的 ApplicationListener 的 onApplicationEvent 方法 ,這里除了會:

篩選的方法如下,都是調用了對應類型的 supportsEventType 方法 :

如圖,我們可以看到對 org.springframework.boot.context.event.ApplicationStartingEvent 感興趣的有5個 Listener

環境准備的具體方法如下:

首先是調用 getOrCreateEnvironment 方法來創建 environment ,我們跟進去可以發現這里是根據我們上面設置的環境的類型來進行選擇的,當前環境會創建 StandardServletEnvironment

我們先來看一下 StandardServletEnvironment 的類繼承關系圖,我們可以看出他是繼承了 AbstractEnvironment :

他會調用子類的 customizePropertySources 方法實現,首先是 StandardServletEnvironment 的實現如下,他會添加 servletConfigInitParams , servletContextInitParams , jndiProperties 三種 properties,當前調試環境沒有配置 jndi properties,所以這里不會添加。接著調用父類的 customizePropertySources 方法,即調用到了 StandardEnvironment 。

我們看一下 StandardEnvironment#customizePropertySources 方法,與上面的三個 properties 創建不同,這兩個是會進行賦值的,包括系統環境變數放入 systemEnvironment 中,jvm 先關參數放到 systemProperties 中:

這里會添加 systemEnvironment 和 systemProperties 這兩個 properties,最終拿到的 properties 數量如下 4個:

在創建完成 Environment 後,接下來就到了調用 configureEnvironment 方法:

我們先看一下 configurePropertySources 方法,這里主要分兩部分,首先是查詢當前是否存在 defaultProperties ,假如不為空就會添加到 environment 的 propertySources 中,接著是處理命令行參數,將命令行參數作為一個 CompositePropertySource 或則 添加到 environment 的 propertySources 裡面,

接著調用 ConfigurationPropertySources#attach 方法,他會先去 environment 中查找 configurationProperties , 假如尋找到了,先檢查 configurationProperties 和當前 environment 是否匹配,假如不相等,就先去除,最後添加 configurationProperties 並將其 sources 屬性設置進去。

回到我們的 prepareEnvironment 邏輯,下一步是通知觀察者,發送 事件,調用的是 SpringApplicationRunListeners#environmentPrepared 方法,最終回到了 #multicastEvent 方法,我們通過 debug 找到最後對這個時間感興趣的 Listener 如下:

其主要邏輯如下:

這個方法最後載入了 PropertySourceLoader , 這里主要是兩種,一個是用於 Properties 的,一個是用於 YAML 的如下:

其中 apply 方法主要是載入 defaultProperties ,假如已經存在,就進行替換,而替換的目標 PropertySource 就是 load 這里最後的一個 consumer 函數載入出來的,這里列一下主要做的事情:
1、載入系統中設置的所有的 Profile 。
2、遍歷所有的 Profile ,假如是默認的 Profile , 就將這個 Profile 加到 environment 中。
3、調用load 方法,載入配置,我們深入看一下這個方法:

他會先調用 getSearchLocations 方法,載入所有的需要載入的路徑,最終有如下路徑:

其核心方法是遍歷所有的 propertySourceLoader ,也就是上面載入到兩種 propertySourceLoader ,最紅 loadForFileExtension 方法,載入配置文件,這里就不展開分析了,說一下主要的作用,因為每個 propertySourceLoader 都有自己可以載入的擴展名,默認擴展名有如下四個 properties, xml, yml, yaml,所以最終拿到文件名字,然後通過 - 拼接所有的真實的名字,然後加上路徑一起載入。

接下來,我們分析 BackgroundPreinitializer ,這個方法在接收 ApplicationPrepareEnvironment 事件的時候真正調用了這份方法:

1、 ConversionServiceInitializer 主要負責將包括 日期,貨幣等一些默認的轉換器注冊到 formatterRegistry 中。
2、 ValidationInitializer 創建 validation 的匹配器。
3、 MessageConverterInitializer 主要是添加了一些 http 的 Message Converter。
4、 JacksonInitializer 主要用於生成 xml 轉換器的。
接著回到我們將的主體方法, prepareEnvironment 在調用完成 listeners.environmentPrepared(environment) 方法後,調用 bindToSpringApplication(environment) 方法,將 environment 綁定到 SpirngApplication 中。
接著將 enviroment 轉化為 StandardEnvironment 對象。
最後將 configurationProperties 加入到 enviroment 中, configurationProperties 其實是將 environment 中其他的 PropertySource 重新包裝了一遍,並放到 environment 中,這里主要的作用是方便 進行解析。

它主要是檢查是否存在 spring.beaninfo.ignore 配置,這個配置的主要作用是設置 javaBean 的內省模式,所謂內省就是應用程序在 Runtime 的時候能檢查對象類型的能力,通常也可以稱作運行時類型檢查,區別於反射主要用於修改類屬性,內省主要用戶獲取類屬性。那麼我們什麼時候會使用到內省呢,java主要是通過內省工具 Introspector 來完成內省的工作,內省的結果通過一個 Beaninfo 對象返回,主要包括類的一些相關信息,而在 Spring中,主要是 BeanUtils#Properties 會使用到,Spring 對內省機制還進行了改進,有三種內省模式,如下圖中紅色框框的內容,默認情況下是使用 USE_ALL_BEANINFO。假如設置為true,就是改成第三中 IGNORE_ALL_BEANINFO

首先是檢查 Application的類型,然後獲取對應的 ApplicationContext 類,我們這里是獲取到了 org.springframework.boot.web.servlet.context. 接著調用 BeanUtils.instantiateClass(contextClass); 方法進行對象的初始化。

最終其實是調用了 的默認構造方法。我們看一下這個方法做了什麼事情。這里只是簡單的設置了一個 reader 和一個 scanner,作用於 bean 的掃描工作。

我們再來看一下這個類的繼承關系

這里獲取 ExceptionReporter 的方式主要還是和之前 Listener 的方式一致,通過 getSpringFactoriesInstances 來獲取所有的 SpringBootExceptionReporter 。

其主要方法執行如下:

2. 如何評價spring源碼深度解析

您好,希望以下回答能幫助您 《SPRING技術內幕——深入解析SPRING架構與設計原理》 該書講了spring的ioc容器原理,在xml的spring配置文件中,對象是如何解析並生成的。 spring的aop,面向切面編程。這兩塊是比較重要的,屬於核心部分。 其他的如spring mvc ,spring jdbc與hibernate,ibatise集成,spring事務,spring security, spring 任務調度都有介紹。 大體來說,屬於跟著代碼走向,一個類一個類介紹了一下。其實代碼都是有英文注釋的。 跟著作都的思路看過來也還是可以的,最好是對照類圖分析。 如您還有疑問可繼續追問。

3. 《spring源碼深度解析第二版高清》pdf下載在線閱讀全文,求百度網盤雲資源

《spring源碼深度解析第二版高清》網路網盤pdf最新全集下載:
鏈接: https://pan..com/s/1k5SzFRYLbqE5Febp-v4bUA

?pwd=ht4h 提取碼: ht4h
簡介:從核心實現和企業應用兩個方面,由淺入深、由易到難地對Spring源碼展開了系統的講解,包括Spring的設計理念和整體架構、容器的基本實現等內容都有介紹。

4. 《看透SpringMVC源代碼分析與實踐》pdf下載在線閱讀全文,求百度網盤雲資源

《看透SpringMVC源代碼分析與實踐》(韓路彪)電子書網盤下載免費在線閱讀

鏈接: https://pan..com/s/1vSy3Wd53qe91ak602kSGqw

提取碼: i1qy

書名:看透SpringMVC源代碼分析與實踐

作者:韓路彪

出版社:機械工業出版社

副標題:源代碼分析與實踐

原作名:韓路彪

出版年:2016-1-1

頁數:309

內容簡介

國內資深Web開發專家根據Spring MVC全新技術撰寫,基於實際生產環境,從基礎知識、源代碼和實戰3個維度對Spring MVC的結構和實現進行詳細講解

全面介紹Spring MVC的架構、原理、核心概念和操作,通過案例完整呈現Tomcat的實現,系統總結Spring MVC九大組件的處理以及常用的技巧和實踐

在大型網站和復雜系統的開發中,Java具有天然的優勢,而在Java的Web框架中Spring MVC以其強大的功能以及簡單且靈活的用法受到越來越多開發者的青睞。本書不僅詳細地分析Spring MVC的結構及其實現細節,而且講解網站的不同架構及其演變的過程,以及網路底層協議的概念及其實現方法,幫助讀者開發更高效的網站。

作者簡介

韓路彪當代知名作家。

5. SpringSecurity源碼整體解析

spring對請求的處理過程如下:

而security所有認證邏輯作為特殊的一個Filter加入到spring處理servelet的過濾鏈中,即FilterChainProxy;
而這個FilterChainProxy內部又有多個過濾器鏈FilterChain,每個鏈有matcher,用於匹配請求,請求來時選擇最先匹配的一條過濾器鏈做許可權認證,每條過濾器鏈又由多個多濾器Filter依序連接而成。如下圖:

configuator配置類裝配到builder類中,builder類藉助confuguator類構造filter或者filterChain
SpringSecurity有兩個重要的builder:

首先生成FilterChainProxy實例,將FilterChainProxy實例再封裝到DelegatingFilterProxy(java web的標准過濾器),作為一個web的Filter再注冊到spring上下文。

至於this.webSecurity.build()內部怎麼實現的,後面再講。

將FilterChainProxy再封裝到DelegatingFilterProxy,在注入到spring上下文。有兩種方式,如下:

上面講到了通過調用this.webSecurity.build()方法產生FilterChainProxy實例,現在仔細分析具體怎麼實現的。

由上可見,主要是init、configure、performBuild三個方法
2.1. init(WebSecurity)
會遍歷所有之前載入好的配置類configuator(adaptor),調用其init。

其中配置類的init方法,主要是構造了HttpSecurity,放入到securityFilterChainBuilders;並在postBuild之後設置inteceptor到websecurity。可見WebSecurityConfigurerAdapter類的init方法實現:

2.2. configure(WebSecurity)
會遍歷所有之前載入好的配置類configuator(adaptor),調用其configure

配置類的configure方法,主要是配置上一步構造好的HttpSecurity實例,將其相關的configuator配置類裝配到HttpSecurity實例。可見WebSecurityConfigurerAdapter類的configure方法實現:

我們在定義自己的過濾器鏈的時候,可以繼承WebSecurityConfigurerAdapter重寫configure方法,比如:

2.3. performBuild(WebSecurity)
遍歷securityFilterChainBuilders(其實就是HttpSecurity)列表調用其build方法,生成SecurityFilterChain實例,最後利用多個SecurityFilterChain實例組成List,再封裝到FilterChainProxy。

securityFilterChainBuilders(其實就是HttpSecurity)的build方法,內部最後也是調用自己的init、configure、performBuild。

2.3.1. init(HttpSecurity)
會遍歷所有之前載入好的配置類configuator,調用其init
配置類的init一般是配置HttpSecurity。以HttpBasicConfigurer配置類為例:

2.3.2. configure(HttpSecurity)
會遍歷所有之前載入好的配置類configuator,調用其configure
配置類的configure一般構造Filter,添加到HttpSecurity的Filter列表中,作為過濾器鏈的其中一個。以HttpBasicConfigurer配置類為例:

2.3.3. performBuild(HttpSecurity)
將List<Filter>以及matcher封裝成SecurityFilterChain

請求到達的時候,FilterChainProxy的dofilter()方法內部,會遍歷所有的SecurityFilterChain,匹配url,第一個匹配到之後則調用該SecurityFilterChain中的List<filter>依次做認證或鑒權,執行最後調用外層傳入的filterchain,將控制權交給上層過濾鏈,即spring 的過濾鏈。
核心邏輯如下:

6. 詳解Spring mvc工作原理及源碼分析

Model 模型層 (javaBean組件 = 領域模型(javaBean) + 業務層 + 持久層)

View 視圖層( html、jsp…)

Controller 控制層(委託模型層進行數據處理)

springmvc是一個web層mvc框架,類似struts2。

springmvc是spring的部分,其實就是spring在原有基礎上,又提供了web應用的mvc模塊。

實現機制:

struts2是基於過濾器實現的。

springmvc是基於servlet實現的。

運行速度:

因為過濾器底層是servlet,所以springmvc的運行速度會稍微比structs2快。

struts2是多例的

springmvc單例的

參數封裝:

struts2參數封裝是基於屬性進行封裝。

springmvc是基於方法封裝。顆粒度更細。

⑴ 用戶發送請求至DispatcherServlet。

⑵ DispatcherServlet收到請求調用HandlerMapping查詢具體的Handler。

⑶ HandlerMapping找到具體的處理器(具體配置的是哪個處理器的實現類),生成處理器對象及處理器攔截器(HandlerExcutorChain包含了Handler以及攔截器集合)返回給DispatcherServlet。

⑷ DispatcherServlet接收到HandlerMapping返回的HandlerExcutorChain後,調用HandlerAdapter請求執行具體的Handler(Controller)。

⑸ HandlerAdapter經過適配調用具體的Handler(Controller即後端控制器)。

⑹ Controller執行完成返回ModelAndView(其中包含邏輯視圖和數據)給HandlerAdaptor。

⑺ HandlerAdaptor再將ModelAndView返回給DispatcherServlet。

⑻ DispatcherServlet請求視圖解析器ViewReslover解析ModelAndView。

⑼ ViewReslover解析後返回具體View(物理視圖)到DispatcherServlet。

⑽ DispatcherServlet請求渲染視圖(即將模型數據填充至視圖中) 根據View進行渲染視圖。

⑾ 將渲染後的視圖返回給DispatcherServlet。

⑿ DispatcherServlet將響應結果返回給用戶。

(1)前端控制器DispatcherServlet(配置即可)

功能:中央處理器,接收請求,自己不做任何處理,而是將請求發送給其他組件進行處理。DispatcherServlet 是整個流程的控制中心。

(2)處理器映射器HandlerMapping(配置即可)

功能:根據DispatcherServlet發送的url請求路徑查找Handler

常見的處理器映射器:BeanNameUrlHandlerMapping,SimpleUrlHandlerMapping,

,(不建議使用)

(3)處理器適配器HandlerAdapter(配置即可)

功能:按照特定規則(HandlerAdapter要求的規則)去執行Handler。

通過HandlerAdapter對處理器進行執行,這是適配器模式的應用,通過擴展多個適配器對更多類型的處理器進行執行。

常見的處理器適配器:HttpRequestHandlerAdapter,,

(4)處理器Handler即Controller(程序猿編寫)

功能:編寫Handler時按照HandlerAdapter的要求去做,這樣適配器才可以去正確執行Handler。

(5)視圖解析器ViewReslover(配置即可)

功能:進行視圖解析,根據邏輯視圖名解析成真正的視圖。

ViewResolver負責將處理結果生成View視圖,ViewResolver首先根據邏輯視圖名解析成物理視圖名即具體的頁面地址,再生成View視圖對象,最後對View進行渲染將處理結果通過頁面展示給用戶。

springmvc框架提供了多種View視圖類型,如:jstlView、freemarkerView、pdfView...

(6)視圖View(程序猿編寫)

View是一個介面,實現類支持不同的View類型(jsp、freemarker、pdf...)

引入相關依賴:spring的基本包、springmvc需要的spring-webmvc,日誌相關的slf4j-log4j12,jsp相關的jstl、servlet-api、jsp-api。

因為DispatcherServlet本身就是一個Servlet,所以需要在web.xml配置。

一、使用默認載入springmvc配置文件的方式,必須按照以下規范:

①命名規則:-servlet.xml ====> springmvc-servlet.xml

②路徑規則:-servlet.xml必須放在WEB-INF下邊

二、如果要不按照默認載入位置,則需要在web.xml中通過標簽來指定springmvc配置文件的載入路徑,如上圖所示。

將自定義的 Controller 處理器配置到 spring 容器中交由 spring 容器來管理,因為這里的 springmvc.xml 配置文件中處理器映射器配置的是 BeanNameUrlHandlerMapping ,根據名字可知這個處理器映射器是根據 bean (自定義Controller) 的 name 屬性值url去尋找執行類 Handler(Controller) , 所以bean的name屬性值即是要和用戶發送的請求路徑匹配的 url 。

根據視圖解析路徑:WEB-INF/jsps/index.jsp

功能:根據bean(自定義Controller)的name屬性的url去尋找執行類Controller。

功能:自定義的處理器(Controller)實現了Controller介面時,適配器就會執行Controller的具體方法。

會自動判斷自定義的處理器(Controller)是否實現了Controller介面,如果是,它將會自動調用處理器的handleRequest方法。

Controller介面中有一個方法叫handleRequest,也就是處理器方法。

因此,自定義的Controller要想被調用就必須實現Controller介面,重寫Controller介面中的處理器方法。

7. Spring Tx源碼解析(二)

   上一篇 我們介紹了 spring-tx 中的底層抽象,本篇我們一起來看看圍繞這些抽象概念 spring-tx 是如何打造出聲明式事務的吧。籠統的說, spring-tx-5.2.6.RELEASE 的實現主要分為兩個部分:

這兩部分彼此獨立又相互成就,並且每個部分都有著大量的源碼支撐,本篇我們先來分析 spring-tx 中的AOP部分吧。

   EnableTransactionManagement 註解想必大家都很熟悉了,它是啟用 Spring 中注釋驅動的事務管理功能的關鍵。

EnableTransactionManagement 註解的主要作用是向容器中導入 ,至於註解中定義的幾個屬性在 Spring AOP源碼解析 中有過詳細分析,這里就不再贅述了。

由於我們並沒有使用 AspectJ ,因此導入容器的自然是 這個配置類。

  這個配置類的核心是向容器中導入一個類型為 的Bean。這是一個 PointcutAdvisor ,它的 Pointcut 是 , Advice 是 TransactionInterceptor 。

   利用 TransactionAttributeSource 解析 @Transactional 註解的能力來選取標注了 @Transactional 註解的方法,而 TransactionInterceptor 則根據應用提出的需求(來自對 @Transactional 註解的解析)將方法增強為事務方法,因此 可以識別出那些標注了 @Transactional 註解的方法,為它們應用上事務相關功能。

   TransactionInterceptor 能對方法進行增強,但是它卻不知道該如何增強,比如是為方法新開一個獨立事務還是沿用已有的事務?什麼情況下需要回滾,什麼情況下不需要?必須有一個『人』告訴它該如何增強,這個『人』便是 TransactionAttributeSource 。

   @Transactional 註解定義了事務的基礎信息,它表達了應用程序期望的事務形態。 TransactionAttributeSource 的主要作用就是解析 @Transactional 註解,提取其屬性,包裝成 TransactionAttribute ,這樣 TransactionInterceptor 的增強便有了依據。

前面我們已經見過, spring-tx 使用 來做具體的解析工作,其父類 定義了解析 TransactionAttribute 的優先順序,核心方法是 computeTransactionAttribute(...) 。

默認只解析 public 修飾的方法,這也是導致 @Transactional 註解失效的一個原因,除此之外它還實現了父類中定義的兩個模板方法:

同時為了支持 EJB 中定義的 javax.ejb.TransactionAttribute 和 JTA 中定義的 javax.transaction.Transactional 註解, 選擇將實際的提取工作代理給 TransactionAnnotationParser 。Spring 提供的 @Transactional 註解由 進行解析。

的源碼還是很簡單的,它使用 AnnotatedElementUtils 工具類定義的 find 語義來獲取 @Transactional 註解信息。 RuleBasedTransactionAttribute 中 rollbackOn(...) 的實現還是挺有意思的,其它的都平平無奇。

RollbackRuleAttribute 是用來確定在發生特定類型的異常(或其子類)時是否應該回滾,而 NoRollbackRuleAttribute 繼承自 RollbackRuleAttribute ,但表達的是相反的含義。 RollbackRuleAttribute 持有某個異常的名稱,通過 getDepth(Throwable ex) 演算法來計算指定的 Throwable 和持有的異常在繼承鏈上的距離。

  程序猿只有在拿到需求以後才能開工, TransactionInterceptor 也一樣,有了 TransactionAttributeSource 之後就可以有依據的增強了。觀察類圖, TransactionInterceptor 實現了 MethodInterceptor 介面,那麼自然要實現介面中的方法:

可以看到, TransactionInterceptor 本身是沒有實現任何邏輯的,它更像一個適配器。這樣分層以後, TransactionAspectSupport 理論上就可以支持任意類型的 Advice 而不只是 MethodInterceptor 。實現上 TransactionAspectSupport 確實也考慮了這一點,我們馬上就會看到。

invokeWithinTransaction(...) 的流程還是非常清晰的:

第一步前文已經分析過了,我們來看第二步。

TransactionInfo 是一個非常簡單的類,我們就不費什麼筆墨去分析它了。接著看第三步,這一步涉及到兩個不同的操作——提交或回滾。

至此, TransactionInterceptor 於我們而言已經沒有任何秘密了。

  本篇我們一起分析了 spring-tx 是如何通過 spring-aop 的攔截器將普通方法增強為事務方法的,下篇就該說道說道 PlatformTransactionManager 抽象下的事務管理細節啦,我們下篇再見~~

8. Spring系列(一)Spring MVC bean 解析、注冊、實例化流程源碼剖析

最近在使用Spring MVC過程中遇到了一些問題,網上搜索不少帖子後雖然找到了答案和解決方法,但這些答案大部分都只是給了結論,並沒有說明具體原因,感覺總是有點不太滿意。

更重要的是這些所謂的結論大多是抄來抄去,基本源自一家,真實性也有待考證。

那作為程序員怎麼能知其所以然呢?

此處請大家內心默讀三遍。

用過Spring 的人都知道其核心就是IOC和AOP,因此要想了解Spring機制就得先從這兩點入手,本文主要通過對IOC部分的機制進行介紹。

在開始閱讀之前,先准備好以下實驗材料。

IDEA 是一個優秀的開發工具,如果還在用Eclipse的建議切換到此工具進行。

IDEA有很多的快捷鍵,在分析過程中建議大家多用Ctrl+Alt+B快捷鍵,可以快速定位到實現函數。

Spring bean的載入主要分為以下6步:

查看源碼第一步是找到程序入口,再以入口為突破口,一步步進行源碼跟蹤。

Java Web應用中的入口就是web.xml。

在web.xml找到ContextLoaderListener ,此Listener負責初始化Spring IOC。

contextConfigLocation參數設置了bean定義文件地址。

下面是ContextLoaderListener的官方定義:

翻譯過來ContextLoaderListener作用就是負責啟動和關閉Spring root WebApplicationContext。

具體WebApplicationContext是什麼?開始看源碼。

從源碼看出此Listener主要有兩個函數,一個負責初始化WebApplicationContext,一個負責銷毀。

繼續看initWebApplicationContext函數。

在上面的代碼中主要有兩個功能:

進入CreateWebAPPlicationContext函數

進入determineContextClass函數。

進入函數。

WebApplication Context有很多實現類。 但從上面determineContextClass得知此處wac實際上是XmlWebApplicationContext類,因此進入XmlWebApplication類查看其繼承的refresh()方法。

沿方法調用棧一層層看下去。

獲取beanFactory。

beanFactory初始化。

載入bean。

讀取XML配置文件。

XmlBeanDefinitionReader讀取XML文件中的bean定義。

繼續查看loadBeanDefinitons函數調用棧,進入到XmlBeanDefinitioReader類的loadBeanDefinitions方法。

最終將XML文件解析成Document文檔對象。

上一步完成了XML文件的解析工作,接下來將XML中定義的bean注冊到webApplicationContext,繼續跟蹤函數。

用BeanDefinitionDocumentReader對象來注冊bean。

解析XML文檔。

循環解析XML文檔中的每個元素。

下面是默認命名空間的解析邏輯。

不明白Spring的命名空間的可以網上查一下,其實類似於package,用來區分變數來源,防止變數重名。

這里我們就不一一跟蹤,以解析bean元素為例繼續展開。

解析bean元素,最後把每個bean解析為一個包含bean所有信息的BeanDefinitionHolder對象。

接下來將解析到的bean注冊到webApplicationContext中。接下繼續跟蹤registerBeanDefinition函數。

跟蹤registerBeanDefinition函數,此函數將bean信息保存到到webApplicationContext的beanDefinitionMap變數中,該變數為map類型,保存Spring 容器中所有的bean定義。

Spring 實例化bean的時機有兩個。

一個是容器啟動時候,另一個是真正調用的時候。

相信用過Spring的同學們都知道以上概念,但是為什麼呢?

繼續從源碼角度進行分析,回到之前XmlWebApplication的refresh()方法。

可以看到獲得beanFactory後調用了 ()方法,繼續跟蹤此方法。

預先實例化單例類邏輯。

獲取bean。

doGetBean中處理的邏輯很多,為了減少干擾,下面只顯示了創建bean的函數調用棧。

創建bean。

判斷哪種動態代理方式實例化bean。

不管哪種方式最終都是通過反射的形式完成了bean的實例化。

我們繼續回到doGetBean函數,分析獲取bean的邏輯。

上面方法中首先調用getSingleton(beanName)方法來獲取單例bean,如果獲取到則直接返回該bean。方法調用棧如下:

getSingleton方法先從singletonObjects屬性中獲取bean 對象,如果不為空則返回該對象,否則返回null。

那 singletonObjects保存的是什麼?什麼時候保存的呢?

回到doGetBean()函數繼續分析。如果singletonObjects沒有該bean的對象,進入到創建bean的邏輯。處理邏輯如下:

下面是判斷容器中有沒有注冊bean的邏輯,此處beanDefinitionMap相信大家都不陌生,在注冊bean的流程里已經說過所有的bean信息都會保存到該變數中。

如果該容器中已經注冊過bean,繼續往下走。先獲取該bean的依賴bean,如果鑹子依賴bean,則先遞歸獲取相應的依賴bean。

依賴bean創建完成後,接下來就是創建自身bean實例了。

獲取bean實例的處理邏輯有三種,即Singleton、Prototype、其它(request、session、global session),下面一一說明。

如果bean是單例模式,執行此邏輯。

獲取單例bean,如果已經有該bean的對象直接返回。如果沒有則創建單例bean對象,並添加到容器的singletonObjects Map中,以後直接從singletonObjects直接獲取bean。

把新生成的單例bean加入到類型為MAP 的singletonObjects屬性中,這也就是前面singletonObjects()方法中獲取單例bean時從此Map中獲取的原因。

Prototype是每次獲取該bean時候都新建一個bean,因此邏輯比較簡單,直接創建一個bean後返回。

從相應scope獲取對象實例。

判斷scope,獲取實例函數邏輯。

在相應scope中設置實例函數邏輯。

以上就是Spring bean從無到有的整個邏輯。

從源碼角度分析 bean的實例化流程到此基本接近尾聲了。

回到開頭的問題,ContextLoaderListener中初始化的WebApplicationContext到底是什麼呢?

通過源碼的分析我們知道WebApplicationContext負責了bean的創建、保存、獲取。其實也就是我們平時所說的IOC容器,只不過名字表述不同而已。

本文主要是講解了XML配置文件中bean的解析、注冊、實例化。對於其它命名空間的解析還沒有講到,後續的文章中會一一介紹。

希望通過本文讓大家在以後使用Spring的過程中有「一切盡在掌控之中」的感覺,而不僅僅是稀里糊塗的使用。

熱點內容
淘寶圖片緩存怎麼關閉 發布:2025-08-21 09:51:31 瀏覽:149
訪問學者出國手續 發布:2025-08-21 09:51:26 瀏覽:365
91資源解壓 發布:2025-08-21 09:42:14 瀏覽:415
創新科存儲技術 發布:2025-08-21 09:42:12 瀏覽:891
我的世界十三區伺服器地址 發布:2025-08-21 09:41:28 瀏覽:432
編程語言排行榜2016 發布:2025-08-21 09:31:33 瀏覽:295
編程教學廣告 發布:2025-08-21 09:28:24 瀏覽:173
安卓手機用哪個軟體編輯視頻 發布:2025-08-21 09:25:02 瀏覽:577
邁騰最低配有哪些值得選裝的配置 發布:2025-08-21 09:14:04 瀏覽:836
php求職 發布:2025-08-21 09:14:03 瀏覽:490