當前位置:首頁 » 操作系統 » mvc源碼解析

mvc源碼解析

發布時間: 2023-02-22 16:16:01

① [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 。

其主要方法執行如下:

② 《看透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的結構及其實現細節,而且講解網站的不同架構及其演變的過程,以及網路底層協議的概念及其實現方法,幫助讀者開發更高效的網站。

作者簡介

韓路彪當代知名作家。

③ spring mvc 常用註解詳解

前言

現在主流的Web MVC框架除了Struts這個主力 外,其次就是Spring MVC了,因此這也是作為一名程序員需要掌握的主流框架,框架選擇多了,應對多變的需求和業務時,可實行的方案自然就多了。不過要想靈活運用Spring MVC來應對大多數的Web開發,就必須要掌握它的配置及原理。

Spring mvc 介紹

Spring Web MVC是一種基於Java的實現了Web MVC設計模式的請求驅動類型的輕量級Web框架,即使用了MVC架構模式的思想,將web層進行職責解耦,基於請求驅動指的就是使用請求-響應模型,框架的目的就是幫助我們簡化開發,Spring Web MVC也是要簡化我們日常Web開發

image.png

spring mvc 常用註解詳解

@Controller

在SpringMVC 中,控制器Controller 負責處理由DispatcherServlet 分發的請求,它把用戶請求的數據經過業務處理層處理之後封裝成一個Model ,然後再把該Model 返回給對應的View 進行展示。在SpringMVC 中提供了一個非常簡便的定義Controller 的方法,你無需繼承特定的類或實現特定的介面,只需使用@Controller 標記一個類是Controller ,然後使用@RequestMapping 等一些註解用以定義請求URL 請求和Controller 方法之間的映射,這樣的Controller 就能被外界訪問到。其標記在一個類上,使用它標記的類就是一個SpringMVC Controller 對象。分發處理器將會掃描使用了該註解的類的方法,並檢測該方法是否使用@RequestMapping 註解。@Controller 只是定義了一個控制器類,而使用@RequestMapping 註解的方法才是真正處理請求的處理器。此外我們還需要將controller注冊到spring里

@RequestMapping

RequestMapping是一個用來處理請求地址映射的註解,可用於類或方法上。用於類上,表示類中的所有響應請求的方法都是以該地址作為父路徑,作用於方法上,表明該處理器的請求地址=父路徑+方法上url+method,其擁有6個屬性

1、 value, method;定義處理器訪問的具體體質

value: 指定請求的實際地址,指定的地址可以是URI Template 模式;

method: 指定請求的method類型, GET、POST、PUT、DELETE等;

2、consumes,proces 定義處理器內容類型

consumes: 指定處理請求的提交內容類型(Content-Type),例如application/json, text/html;

proces: 指定返回的內容類型,僅當request請求頭中的(Accept)類型中包含該指定類型才返回;

3、params,headers 定義處理器處理類型

params: 指定request中必須包含某些參數值,才讓該方法處理!

headers: 指定request中必須包含某些指定的header值,才能讓該方法處理請求。

@PathVariable

用於將請求URL中的模板變數映射到功能處理方法的參數上,即取出uri模板中的變數作為參數。如:

@requestParam

@requestParam主要用於在SpringMVC後台控制層獲取參數,類似一種是request.getParameter("name"),它有三個常用參數:defaultValue = "0", required = false, value = "isApp";defaultValue 表示設置默認值,required 銅過boolean設置是否是必須要傳入的參數,value 值表示接受的傳入的參數類型。

@ResponseBody

作用: 該註解用於將Controller的方法返回的對象,通過適當的HttpMessageConverter轉換為指定格式後,寫入到Response對象的body數據區。使用時機:返回的數據不是html標簽的頁面,而是其他某種格式的數據時(如json等)使用;

@RequestBody

該註解常用來處理Content-Type: 不是application/x-www-form-urlencoded編碼的內容,例如application/json, application/xml等;它是通過使用HandlerAdapter 配置的HttpMessageConverters來解析post data body,然後綁定到相應的bean上的。

spring mvc 攔截器配置

preHandle:預處理回調方法,返回值:true表示繼續流程,false表示流程中斷(如登錄檢查失敗),不會繼續續調用其他的攔截器或處理器,此時我們需要通過response來產生響應;

postHandle:後處理回調方法,實現處理器的後處理(但在渲染視圖之前),此時我們可以通過modelAndView(模型和視圖對象)對模型數據進行處理或對視圖進行處理,modelAndView也可能為null。

afterCompletion:整個請求處理完畢回調方法,即在視圖渲染完畢時回調,如性能監控中我們可以在此記錄結束時間並輸出消耗時間,還可以進行一些資源清理,類似於try-catch-finally中的finally,但僅調用處理器執行鏈中preHandle返回true的攔截器的afterCompletion。

spring mvc 靜態資源放問配置

image.png

spring mvc 文件上傳

前端

後端

spring mvc 工作流程詳解

image.png

1、 用戶發送請求至前端控制器DispatcherServlet。

2、 DispatcherServlet收到請求調用HandlerMapping處理器映射器。

3、 處理器映射器找到具體的處理器(可以根據xml配置、註解進行查找),生成處理器對象及處理器攔截器(如果有則生成)一並返回給DispatcherServlet。

4、 DispatcherServlet調用HandlerAdapter處理器適配器。

5、 HandlerAdapter經過適配調用具體的處理器(Controller,也叫後端控制器)。

6、 Controller執行完成返回ModelAndView。

7、 HandlerAdapter將controller執行結果ModelAndView返回給DispatcherServlet。

8、 DispatcherServlet將ModelAndView傳給ViewReslover視圖解析器。

9、 ViewReslover解析後返回具體View。

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

11、 DispatcherServlet響應用戶。

如果你也對Java架構比如分布式、微服務、源碼分析、性能優化、高並發高可用等技術感興趣可以在手機上面私信我,回復「架構」二字即可免費領取一套價值3880的架構資料哦。

④ SpringMVC

一、SpringMVC應用
1.springmvc是一個表現層的框架
經典三層架構:表現層, service層, 層。
Spring MVC和Struts2一樣,都是 為了解決表現層問題 的web框架,它們都是基於 MVC 設計模
式的。而這些表現層框架的主要職責就是處理前端HTTP請求。
Spring MVC 本質可以認為是對servlet的封裝,簡化了我們serlvet的開發
2.工作流程:
開發過程:
請求處理流程:
九大組件:
3.請求參數綁定,說白了SpringMVC是如何接受參數的:
原生servlet接收一個整型參數:
SpringMVC框架對Servlet的封裝,簡化了servlet的很多操作,SpringMVC框架對Servlet的封裝,簡化了servlet的很多操作
參數綁定:取出參數值綁定到handler⽅法的形參上
默認支持 Servlet API 作為方法參數:
綁定簡單類型參數:
綁定Pojo類型參數:
綁定Pojo包裝對象參數:
綁定日期類型參數(需要配置自定義類型轉換器):
4.Restful風格
什麼是Restful:
什麼是rest:
Restful的優點:
Restful的特性:
Restful的示例:
SpringMVC如何支持Restful風格的請求:
5.Ajax Json交互
交互:兩個方向
什麼是Json:
@ResponseBody註解:
分析SpringMVC使用Json交互:
二、SpringMVC高級技術
1.攔截器(Inteceptor)使用
監聽器、過濾器、攔截器的對比:
攔截器的執行流程:
多個攔截器的執行流程:
自定義SpringMVC攔截器:
2.處理multipart形式的數據
文件上傳:
3.在控制器中處理異常:
4.基於Flash屬性的跨重定向請求數據傳遞:
三、手寫SpringMVC框架:
四、SpringMVC源碼剖析
1.前端控制器 DispatcherServlet 繼承結構:
2.重要時機點分析
SpringMVC處理請求的流程即為:
3.核心步驟getHandler方法剖析:
4.核心步驟getHandlerAdapter方法剖析:
5.核心步驟ha.handle方法剖析:
6.核心步驟processDispatchResult方法剖析:
7.SpringMVC九大組件初始化:
五、SSM整合
1.整合策略:先整合Spring + Mybatis,然後再整合SpringMVC
2.Mybatis整合Spring
3.整合SpringMVC

未完待補充完整。。。

⑤ 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的過程中有「一切盡在掌控之中」的感覺,而不僅僅是稀里糊塗的使用。

⑥ Spring事件監聽機制源碼解析

1.Spring事件監聽體系包括三個組件:事件、事件監聽器,事件廣播器。

事件:定義事件類型和事件源,需要繼承ApplicationEvent。

事件監聽器:用來監聽某一類的事件,並且執行具體業務邏輯,需要實現ApplicationListener 介面或者需要用@ListenerEvent(T)註解。好比觀察者模式中的觀察者。

事件多播器:負責廣播通知所有監聽器,所有的事件監聽器都注冊在了事件多播器中。好比觀察者模式中的被觀察者。Spring容器默認生成的是同步事件多播器。可以自定義事件多播器,定義為非同步方式。

創建 的過程中,會執行refresh()中的()方法。該方法先獲取bean工廠,然後判斷工廠是否包含了beanName 為 applicationEventMulticaster的bean。如果包含了,則獲取該bean,賦值給applicationEventMulticaster 屬性。如果沒有,則創建一個 對象,並且賦值給 applicationEventMulticaster 。實現了源碼如下:

監聽器的注冊有兩種,通過實現 ApplicationListener介面或者添加@EventListener註解。

注冊的邏輯實現在refresh()中的registerListeners()方法裡面。第一步,先獲取當前ApplicationContext中已經添加的 applicationListeners(SpringMVC源碼中有用到),遍歷添加到多播器中。第二步,獲取實現了ApplicationListener介面的listenerBeanNames集合,添加至多播器中。第三步,判斷是否有早期事件,如果有則發起廣播。

思考一下,上面的代碼中第二步為啥添加的是listenerBeanName?

如果監聽器是懶載入的話(即有@Lazy 註解)。那麼在這個時候創建監聽器顯然是不對的,這個時候不能創建監聽器。所以添加監聽器到多播器的具體邏輯放在初始化具體的監聽器之後。通過 BeanPostProcessor 的介面實現。具體的實現類是 ApplicationListenerDetector 。這個類是在 refreah()中prepareBeanFactory()方法中添加的。代碼如下:

在創建 的構造方法中,會執行org.springframework.context.annotation.AnnotationConfigUtils#(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object) 方法。這個方法中會添加兩個 beanDefs, 代碼如下:

EventListenerMethodProcessor:事件監聽器的BeanFactory後置處理器,在前期會創建 DefaultEventListenerFactory ,後期在創建好Bean之後,根據 EventListener 屬性,調用DefaultEventListenerFactory創建具體的 。

DefaultEventListenerFactory:監聽器的創建工廠,用來創建 。

EventListenerMethodProcessor 的類繼承圖如下:

在refreash的()中會調用 org.springframework.context.event.EventListenerMethodProcessor#postProcessBeanFactory方法,獲取EventListenerFactory 類型的 Bean。代碼如下:

在 org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons 方法中,創建完所有的單例Bean 之後,會遍歷所有Bean是否實現了 SmartInitializingSingleton 介面。如果實現介面會執行該 Bean 的 afterSingletonsInstantiated() 方法。代碼如下:

org.springframework.context.event.EventListenerMethodProcessor#afterSingletonsInstantiated 中會調用私有方法 processBean()進行 ApplicationEventAdatper 的創建。代碼如下:

可以通過調用 org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType) 方法進行事件的調用。代碼如下:

中的 multicasEvent,invokeListener,doInvokeListener 三個方法代碼如下:

SpringMVC中就是通過Spring的事件機制進行九大組件的初始化。

監聽器定義在FrameworkServlet類中,作為內部類。代碼如下:

監聽器的添加在org.springframework.web.servlet.FrameworkServlet# 中進行。通過SourceFilteringListener進行包裝。添加代碼如下:

在refresh中的registerListeners方法進行添加,代碼如下:

在refresh中的finishRefresh()方法中,會調用publishEvnet(new ContextRefreshedEvent(this))發布事件。進行多播器廣播,代碼如下

最終會調到FrameworkServlet.this.onApplicationEvent(event)。

⑦ 請問誰有軟體架構師的學習資料分享一下,謝謝。

《架構師必看的精品視頻》網路網盤資源免費下載

鏈接:https://pan..com/s/1U_kS4ZlO9ima57J-vMgAYg

提取碼:jdsm
架構師必看的精品視頻|咕泡學院 - Java設計模式|咕泡學院 - Java 9 新特性之核心庫|青山_20181213_Redis緩存穿透原理與解決方案.mp4|青山_20181209_如何快速搭建一個微服務架構.mp4|青山_20181205_分布式架構核心組件之消息隊列.mp4|咕泡學院 - 微服務實踐之路.mp4|咕泡學院 - 深入淺出 Spring Boot 日誌.mp4|咕泡學院 - 深入淺出 Spring Boot 管控.mp4|咕泡學院 - Reactor Streams 並發編程之 Reactor.mp4|咕泡學院 - Java 9 非同步並發編程.mp4|Tom_20181213_如何設計億級並發的分布式系統.mp4|Tom_20181204_千萬級並發分布式架構從0到1.mp4|Tom_20180730_不復制、不粘貼,一口氣擼完Spring核心代碼.mp4|Tom_20180710_巧用ELK快速實現網站流量監控可視化.mp4

⑧ 軟體架構師應該知道的97件事的內容簡介

《架構師視頻資料》網路網盤資源免費下載

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

提取碼:xkhh
架構師視頻資料|周瑜老師-一線大佬帶你詳解Dubbo核心原理.mp4|周瑜老師-跳槽面試HashMap常見面試題及解答.mp4|周瑜老師-面試必過!Mysql索引底層原理詳解.mp4|周瑜老師-大型互聯網企業分布式ID生成策略介紹.mp4|太白老師-netty內置解碼器詳解.mp4|太白-redis高可用集群.mp4|華安-並發編程面試0906.mp4|華安-jvm內存結構0927.mp4|spring源碼分析-cofigration的原理.mp4|SpringMvc源碼解析-商鞅老師.mp4|springboot-子路.mp4|Spring Cloud 源碼&Eureka源碼.mp4|NIO-子路.mp4|java當中的鎖事.mp4

⑨ 從源碼理解總結web容器、spring容器、spring mvc容器三者關系

本篇,我打算從springMVC項目的web.xml的配置文件入手,通過部分源碼逐步去理解解釋三個容器的關系以及調用順序,因為是基於我個人的理解,可能有所不足。

一般web.xml文件里會有如下兩段配置信息:

我們先了解下web.xml,以下引用自 《web.xml文件是什麼?有什麼用?--詳解》 :

然後結合我們上面的web.xml中關於spring和spring mvc的配置信息來進入話題:

    首先,啟動web容器的時,會先生成對應項目的ServelContent對象,這個是每個項目的上下文,這個ServelContent可以管理所有的servlet,並將我們web.xml中設置的<context-param>內容作為鍵值對交給這個對象。

    然後載入<listener>標簽內容,這個時候就會產生org.springframework.web.context.ContextLoaderListener。

spring的這個 ContextLoaderListener 在接下來的過程中很重要,我們來看一下源碼

首先,可以看出它繼承了ContextLoader類,並實現了ServletContextListener介面。

這里再直接引用他人的結論   《Spring中ContextLoaderListener作用》

好了,人家說法中回到我們的起點了,我們基本都被人告知「ContextLoaderListener的作用是創建並初始化spring容器」

那我們就可以深入進去看看,到底哪裡做了這一步:

首先,我們知道了ServletContextListene是ServletContext的監聽者,監聽器的響應動作就是在伺服器啟動時contextInitialized會被調用,關閉的時候contextDestroyed被調用,這個好理解,那我們就來看一下ContextLoaderListener重寫的contextInitialized方法到底做了什麼。

我們再進入觀察initWebApplicationContext方法細看

我因為自己消化過一遍,直接給出關鍵位置的方法說明——

1、首先是278行:創建了WebApplicationContext,我們可以理解為spring容器的殼子有了

2、其次是288和289行:對ApplicationContext載入了配置文件,並設置servletContext為WebApplicationContext的parent,到這一步,可以理解為我們的spring容器也就差不多成型了

3、接下來是294行:把ApplicationContext對象以鍵值對的形式存到servletContext中,這一步很關鍵,就是因為servletContext中存在這個鍵值對,所以其他內部成員可以通過servletContext訪問到ApplicationContext,當然也能使用其管理的bean,而spring mvc則沒有這樣存在servletContext,所以我覺得正是這一步決定了子容器springmvc可以取用父容器內的bean,反著則不然。

接下來直到輪到我們的springmvc容器<servlet>標簽內容

會生成控制org.springframework.web.servlet.DispatcherServlet,這是一個前端控制器,主要的內容我之前也有一篇文章做過自我記錄

《Spring MVC的工作機制簡單理解》

我們可以看到設置的

<load-on-startup>1</load-on-startup>

這個標簽大概意思就是:

1、load-on-startup 元素標記容器是否應該在web應用程序啟動的時候就載入這個servlet,(實例化並調用其init()方法)。

2、它的值必須是一個整數,表示servlet被載入的先後順序。

3、如果該元素的值為負數或者沒有設置,則容器會當Servlet被請求時再載入。

4、如果值為正整數或者0時,表示容器在應用啟動時就載入並初始化這個servlet,值越小,servlet的優先順序越高,就越先被載入。值相同時,容器就會自己選擇順序來載入。

在DispatcherServlet的時候就根據springMVC容器容器的配置文件生成。

比如我這邊就是

那順序確定了,我們再看一下spring和spring mvc的父子關系哪裡確定:

我們可以從下面3個截圖看到dispatcherServlet的繼承關系,同時,init方法用的是dispatcherServlet父類的父類的方法。

重點在於initServletBean()方法,經過追蹤,我們找到該方法的最終實現又是在dispatcherServlet的父類FrameworkServlet中

其中涉及父子關系的實際是在219行的initWebApplicationContext()方法

initWebApplicationContext()方法主要用於創建或刷新WebApplicationContext實例,並對Servlet功能所使用的變數進行初始化。

從238行源碼就可以看到,它獲得ContextLoaderListener中初始化的rootContext,

在246行設置了父子關系的引用,也就是從這一點我們看到了spring和springMVC的父子關系!

並且,可以看到這只是一條單向的引用,spring中沒有引用直接指向springMVC,也就是子類能找到父類,然而父類都不知道這個子類,父子容器之間內部對象調用關系更明了。

再通過構造函數和Servlet的contextAttribute屬性查找ServletContext來進行webApplicationContext實例的初始化,最終。

這個方法內263行源碼onRefresh(wac)方法是FrameworkServlet提供的模板方法,在子類,也就是我們的DispatcherServlet的onRefresh()方法中進行了重寫。而在onRefresh()方法中調用了initStrategies()方法來完成初始化工作,初始化Spring MVC的9個組件。

1、Tomcat在啟動時給每個Web應用創建一個全局的上下文環境,這個上下文就是ServletContext,其為後面的Spring容器提供環境。

2、Tomcat在啟動過程中觸發容器初始化事件,Spring的ContextLoaderListener會監聽到這個事件,它的contextInitialized方法會被調用,在這個方法中,Spring會初始化全局的Spring根容器,這個就是Spring的IoC容器,IoC容器初始化完畢後,Spring將其存儲到ServletContext中,便於以後來獲取。

3、Tomcat在啟動過程中還會掃描Servlet,一個Web應用中的Servlet可以有多個,以SpringMVC中的DispatcherServlet為例,這個Servlet實際上是一個標準的前端控制器,用以轉發、匹配、處理每個Servlet請求。

4、Servlet會在容器啟動時載入或延遲載入(根據啟動級別設置數字)。延遲載入時,當第一個請求達到時,serlet容器發現對應Servlet還沒有被實例化,就調用Servlet的init方法。

在spring MVC里

        DispatcherServlet在初始化的時候會建立自己的容器,叫做SpringMVC 容器,用來持有Spring MVC相關的Bean。同時,Spring MVC還會通過ServletContext拿到Spring根容器,並將Spring根容器設為SpringMVC容器的父容器,請注意,Spring MVC容器可以訪問父容器中的Bean,但是父容器不能訪問子容器的Bean, 也就是說Spring根容器不能訪問SpringMVC容器里的Bean。

        說的通俗點就是,在Controller里可以訪問Service對象,但是在Service里不可以訪問Controller對象。

熱點內容
java返回this 發布:2025-10-20 08:28:16 瀏覽:593
製作腳本網站 發布:2025-10-20 08:17:34 瀏覽:888
python中的init方法 發布:2025-10-20 08:17:33 瀏覽:581
圖案密碼什麼意思 發布:2025-10-20 08:16:56 瀏覽:765
怎麼清理微信視頻緩存 發布:2025-10-20 08:12:37 瀏覽:684
c語言編譯器怎麼看執行過程 發布:2025-10-20 08:00:32 瀏覽:1012
郵箱如何填寫發信伺服器 發布:2025-10-20 07:45:27 瀏覽:255
shell腳本入門案例 發布:2025-10-20 07:44:45 瀏覽:113
怎麼上傳照片瀏覽上傳 發布:2025-10-20 07:44:03 瀏覽:806
python股票數據獲取 發布:2025-10-20 07:39:44 瀏覽:712