Spring 原始碼解析一:SpringMVC 的載入機制
1. spring-framework
包含的模組
在解析 SpringMVC
的載入機制之前,先來看看官方 spring-framework
包含有哪些模組,各有什麼用。
spring-jcl
:spring
框架的通用日誌處理spring-core
:spring
框架的核心機制模組,包括 Java 位元組碼的操作處理與動態生成、依賴注入機制(也叫控制反轉)、工具類庫、
註解操作、編碼處理與資料轉換、資源載入與處理、環境載入機制等spring-beans
:spring bean
的定義、載入、解析、編輯等spring-context
:spring
框架的上下文環境,包括應用快取、應用事件、應用配置、核心註解定義與處理、資源載入、非同步與定時任務、資料驗證與格式化等spring-aop
:面向切面程式設計的封裝與處理spring-aspects
:使用 AspectJ 作為底層實現的面向切面程式設計spring-tx
:事務的封裝與處理spring-jdbc
:資料庫連結的封裝與處理spring-context-indexer
:對註解@Indexed
的支援spring-context-support
:對一些第三方庫的可選支援,如ehcache, javamail, quartz, freemarker
spring-oxm
:對 O/X Mapper 的封裝spring-messaging
:對http, rsocket, simp
等訊息傳遞協議的封裝spring-jms
:對 JMS(Java 訊息服務) 的封裝spring-expression
:Spring Expression Language (SpEL) Spring 表示式語言的實現spring-r2dbc
:對 R2DBC 的封裝spring-orm
:對 JPA 和 hibernate 的封裝spring-web
:提供了 Web 框架的基礎結構與技術,如 Http 的呼叫、過濾、處理等spring-webmvc
:Web MVC 架構的實現,包括 Servlet 容器初始化、路由對映、檢視渲染、響應處理等spring-websocket
:對 WebSocket 的支援spring-webflux
:Reactive Web 框架的實現,與spring-webmvc
相對
SpringMVC 框架的核心模組主要是:spring-core
、spring-beans
、spring-context
、spring-web
、spring-webmvc
,後面也主要從這幾個模組來分析。
1.1. spring-core
spring-core
的核心功能有幾點需要在這裡簡單介紹一下:
spring-core
有強大的 Java 位元組碼操作處理功能與動態生成功能,這是面向切面程式設計、資料型別轉換、SpEL 表示式等功能的基礎spring-core
提供了依賴注入機制,這是spring bean
載入的基礎,也是我們可以使用@Autowired
自動裝載物件等功能的底層機制spring-core
提供了環境載入的機制,所以我們可以使用application-dev.yml, application-test.yml, application-prod.yml, ...
來根據環境載入不同的配置spring-core
提供了一個類似 Java SPI 的的擴充套件機制,可以自動例項化其他包指定的類,spring-boot, spring-cloud
都依賴這個機制自動載入資源。
在META-INF/spring.factories
檔案中定義需要自動載入的類,詳細介紹可以參考 Spring Factories
1.2. spring-beans
Spring bean 的載入與擴充套件機制有幾點需要在這裡簡單介紹一下:
- Spring bean 的定義主要是兩種:基於註解的定義、基於 XML 檔案的定義
spring-beans
提供了基於 XML 配置的、第三方對 bean 的自定義擴充套件機制,主要是在META-INF/spring.handlers, META-INF/spring.schemas
檔案中定義需要擴充套件的標籤,比如<dubbo:application name="name"/>, <dubbo:registry address="address"/>
- 基於註解的自定義擴充套件,需要依賴
spring-boot
的擴充套件載入機制
1.3. spring-context
spring-context
是應用的核心處理部分,包括:
- 應用快取
- 應用事件
- 應用配置
- 核心註解定義與處理
- 資源載入
- 非同步與定時任務
- 資料驗證與格式化
等,@ComponentScan, @Profile, @Conditional, @Bean, @Async, @Controller, @Service, @Component, @Validated
等這類框架核心註解便是在這裡定義的。
1.4. spring-web
spring-web
是 Http 的核心處理部分,主要包含:
- 核心 Http 請求與響應處理(包括 Cookie、快取、多媒體等)
- Http 請求與響應編解碼與轉換(包括 Json、XML、ProtoBuf 等)
- Reactive Web 框架基礎處理
- 呼叫客戶端(如
RestTemplate
) - Servlet 上下文環境
- 請求過濾器
- Multipart 檔案上傳處理
等,@RequestMapping, @RequestParam, @PathVariable, @ResponseBody, @RestController
等這類 Web 核心註解便是在這裡定義的。
1.5. spring-webmvc
spring-webmvc
依賴於 spring-web
,主要功能包括:
- Servlet 容器初始化
- 路由對映
- 檢視渲染
- 響應處理
等,如果不使用 Spring MVC ,但想要藉助其它 Spring 支援的 web 相關技術的優勢,那麼只需依賴 spring-web
,如 spring-webflux
。
1.6. spring-webflux
spring-webflux
與 spring-webmvc
相對應,webmvc
是同步阻塞框架,而 webflux
是非同步非阻塞框架,是 Spring Framework 5.0 中引入的新的響應式 web 框架。
參考:Spring WebFlux 入門、Spring WebFlux :: Spring Docs
2. 一個簡單的 spring-webmvc
專案配置
在 WEB-INF/web.xml
檔案中如下配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>springMVC</display-name>
<!-- 部署 DispatcherServlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 容器再啟動時立即載入servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- 處理所有URL -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 定義應用程式監聽器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
這裡有兩個入口類:
servlet-class
org.springframework.web.servlet.DispatcherServlet: 指定用來處理對應 URL 請求的類listener-class
org.springframework.web.context.ContextLoaderListener: 設定事件監聽器,事件監聽程式在建立、修改和刪除會話或 servlet 環境時得到通知
這兩個類分別定義在 spring-webmvc
與 spring-web
中,下面對他們一一進行解析。
3. DispatcherServlet
先來看看 DispatcherServlet
的繼承關係:
- javax.servlet.Servlet
- javax.servlet.GenericServlet
- javax.servlet.http.HttpServlet
- HttpServletBean
- FrameworkServlet
- DispatcherServlet
3.1. javax.servlet.Servlet
javax.servlet.Servlet
主要定義了 2 個方法:
init
:初始化 Servlet,只執行一次service
:響應請求,每次 http 請求都會呼叫這個方法
public interface Servlet {
// 初始化 Servlet,只執行一次
public void init(ServletConfig config) throws ServletException;
// 響應請求,每次http請求都會呼叫這個方法
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
// 銷燬 Servlet
public void destroy();
}
3.2. javax.servlet.GenericServlet
再來看看 javax.servlet.GenericServlet
javax.servlet.GenericServlet
主要是過載了 init
方法
public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable {
public GenericServlet() {}
// 新增配置初始化
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
// 保留無參初始化
public void init() throws ServletException {}
// 留給子類實現
public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
}
3.3. javax.servlet.http.HttpServlet
再來看看 javax.servlet.http.HttpServlet
javax.servlet.http.HttpServlet
主要是過載了 service
方法,並擴充套件了 7 個方法:
doGet
:處理 GET 請求,只輸入錯誤資訊,未實現doHead
:處理 HEAD 請求,只輸入錯誤資訊,未實現doPost
:處理 POST 請求,只輸入錯誤資訊,未實現doPut
:處理 PUT 請求,只輸入錯誤資訊,未實現doDelete
:處理 DELETE 請求,只輸入錯誤資訊,未實現doOptions
:處理 OPTIONS 請求doTrace
:處理 TRACE 請求
public abstract class HttpServlet extends GenericServlet {
private static final String METHOD_DELETE = "DELETE";
private static final String METHOD_HEAD = "HEAD";
private static final String METHOD_GET = "GET";
private static final String METHOD_OPTIONS = "OPTIONS";
private static final String METHOD_POST = "POST";
private static final String METHOD_PUT = "PUT";
private static final String METHOD_TRACE = "TRACE";
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
protected void doHead(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
NoBodyResponse response = new NoBodyResponse(resp);
// 呼叫 doGet,但body設為空body
doGet(req, response);
response.setContentLength();
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// ... 程式碼省略
}
protected void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// ... 程式碼省略
}
protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// ... 程式碼省略
}
protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// ... 程式碼省略
}
protected void doTrace(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// ... 程式碼省略
}
// 實現了GET, HEAD, POST PUT, DELETE, OPTIONS, TRACE七個Http方法
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
// ... 程式碼省略
doGet(req, resp);
// ... 程式碼省略
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
// 把 Servlet 轉化為 HttpServlet
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
service(request, response);
}
}
3.4. HttpServletBean
再來看看 HttpServletBean
HttpServletBean
主要是過載了 init
方法,並擴充套件了 2 個方法:
initBeanWrapper
:初始化由 Servlet Config 定義的 Java Bean,由子類實現,預設不實現initServletBean
:初始化 Servlet bean,由子類實現
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
// 初始化
@Override
public final void init() throws ServletException {
// Set bean properties from init parameters.
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// Let subclasses do whatever initialization they like.
initServletBean();
}
// 初始化由Servlet Config定義的Java Bean,由子類實現,預設不實現
protected void initBeanWrapper(BeanWrapper bw) throws BeansException {
}
// 初始化Servlet bean,由子類實現
protected void initServletBean() throws ServletException {
}
}
3.5. FrameworkServlet
再來看看 FrameworkServlet
FrameworkServlet
是框架的核心 Servlet,主要是過載了 initServletBean
方法,並擴充套件了 2 個方法:
initFrameworkServlet
:初始化框架 Servlet,由子類實現,預設不實現onRefresh
:重新整理上下文資料,由子類實現
過載了 service, doGet, doPost, doPut, doDelete, doOptions, doTrace
方法,並擴充套件了 1 個方法:
doService
:處理響應請求
3.5.1. FrameworkServlet.initServletBean
父類 HttpServletBean
初始化後,留下兩個鉤子 initBeanWrapper, initServletBean
,initBeanWrapper
預設並不實現,所以來看看 initServletBean
鉤子的實現:FrameworkServlet.initServletBean
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
@Override
protected final void initServletBean() throws ServletException {
// ... 程式碼省略
try {
// 初始化Web應用上下文
this.webApplicationContext = initWebApplicationContext();
// 初始化Web框架Servlet
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
// ... 程式碼省略
}
// 初始化框架Servlet,由子類實現,預設不實現
protected void initFrameworkServlet() throws ServletException {}
}
再來看看 FrameworkServlet.initWebApplicationContext
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
protected WebApplicationContext initWebApplicationContext() {
// 獲取應用根上下文
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
// 未啟用
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
// 配置並重新整理應用上下文
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// 如果沒有,則在ServletContext中查詢是否註冊過
wac = findWebApplicationContext();
}
if (wac == null) {
// 如果任然沒有,則以rootContext為父上下文建立一個新的上下文
// 並呼叫 configureAndRefreshWebApplicationContext(cwac) 配置並重新整理新的上下文
// 預設使用 XmlWebApplicationContext(基於XML載入)作為應用上下文
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// 過載上下文資料
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
if (this.publishContext) {
// 把上下文註冊到ServletContext中
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
// 以parent為父上下文建立一個新的上下文
// 並呼叫 configureAndRefreshWebApplicationContext(cwac) 配置並重新整理新的上下文
// 預設使用 XmlWebApplicationContext(基於XML載入)作為應用上下文
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
// 這裡預設使用 XmlWebApplicationContext(基於XML載入)
Class<?> contextClass = getContextClass();
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
configureAndRefreshWebApplicationContext(wac);
return wac;
}
}
這其中有兩個方法需要深入解析:configureAndRefreshWebApplicationContext
, onRefresh
再來看看 FrameworkServlet.configureAndRefreshWebApplicationContext
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
// ... 程式碼省略
// 設定ServletContext
wac.setServletContext(getServletContext());
// 設定ServletConfig
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
// 新增應用事件監聽器,應用事件會觸發當前物件的onApplicationEvent方法
// 進一步,會呼叫當前物件的onRefresh方法,重新整理上下文資料
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// ... 程式碼省略
// 初始化一些需要初始載入的類,呼叫這些類的initialize方法
applyInitializers(wac);
// 應用上下文重新整理
wac.refresh();
}
// 應用事件會觸發此方法,然後呼叫當前物件的onRefresh方法,重新整理上下文資料
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
synchronized (this.onRefreshMonitor) {
onRefresh(event.getApplicationContext());
}
}
}
再來看看 FrameworkServlet.onRefresh
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
protected void onRefresh(ApplicationContext context) {
// 由子類來實現
}
}
3.5.2. FrameworkServlet.service
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
// 如果Http方法是Patch或沒有,擴充套件處理
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 擴充套件處理
processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 擴充套件處理
processRequest(request, response);
}
@Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 擴充套件處理
processRequest(request, response);
}
@Override
protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 擴充套件處理
processRequest(request, response);
}
@Override
protected void doOptions(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) {
// 擴充套件處理
processRequest(request, response);
if (response.containsHeader("Allow")) {
// Proper OPTIONS response coming from a handler - we're done.
return;
}
}
// ... 程式碼省略
}
@Override
protected void doTrace(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (this.dispatchTraceRequest) {
// 擴充套件處理
processRequest(request, response);
if ("message/http".equals(response.getContentType())) {
// Proper TRACE response coming from a handler - we're done.
return;
}
}
super.doTrace(request, response);
}
}
再來看看擴充套件處理方法 FrameworkServlet.processRequest
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// ... 程式碼省略
// 記錄請求屬性與上下文環境,請求處理完後派發事件
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
doService(request, response);
}
// ... 程式碼省略
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
// 由子類來實現
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
}
3.6. DispatcherServlet
DispatcherServlet
主要擴充套件了 2 個方法:onRefresh
、doService
,所以來看看 DispatcherServlet 是如何實現的
3.6.1. DispatcherServlet.onRefresh
public class DispatcherServlet extends FrameworkServlet {
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
// 初始化Multipart檔案上傳處理
initMultipartResolver(context);
// 初始化本地化處理
initLocaleResolver(context);
// 初始化主題處理
initThemeResolver(context);
// 初始化處理器對映
initHandlerMappings(context);
// 初始化處理器適配
initHandlerAdapters(context);
// 初始化處理器異常
initHandlerExceptionResolvers(context);
// 初始化檢視查詢處理
initRequestToViewNameTranslator(context);
// 初始化檢視解析處理
initViewResolvers(context);
// 初始化記憶體暫存session資料管理器
initFlashMapManager(context);
}
private void initMultipartResolver(ApplicationContext context) {
try {
// 獲取bean
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
// ... 程式碼省略
}
catch (NoSuchBeanDefinitionException ex) {
// ... 程式碼省略
}
}
private void initLocaleResolver(ApplicationContext context) {
try {
// 獲取bean
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
// ... 程式碼省略
}
catch (NoSuchBeanDefinitionException ex) {
// ... 程式碼省略
}
}
private void initThemeResolver(ApplicationContext context) {
try {
// 獲取bean
this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
// ... 程式碼省略
}
catch (NoSuchBeanDefinitionException ex) {
// ... 程式碼省略
}
}
private void initFlashMapManager(ApplicationContext context) {
try {
// 獲取bean
this.flashMapManager = context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);
}
catch (NoSuchBeanDefinitionException ex) {
// 沒有bean,則獲取預設策略
this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class);
}
}
}
3.6.1.1. DispatcherServlet.initHandlerMappings
DispatcherServlet.initHandlerMappings
public class DispatcherServlet extends FrameworkServlet {
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
// 預設是探測所有的HandlerMapping,包括父上下文
if (this.detectAllHandlerMappings) {
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
// 否則直接獲取bean
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {}
}
// 如果以上兩種都沒有定義,則獲取預設的處理策略
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
}
// ... 程式碼省略
}
// 獲取預設的處理策略
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
// 嘗試從DispatcherServlet.properties檔案中載入
if (defaultStrategies == null) {
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
}
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
// 建立bean
Object strategy = createDefaultStrategy(context, clazz);
// 裝載到 strategies 中
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
// ... 程式碼省略
}
catch (LinkageError err) {
// ... 程式碼省略
}
}
return strategies;
}
else {
return Collections.emptyList();
}
}
// 建立bean
protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
return context.getAutowireCapableBeanFactory().createBean(clazz);
}
}
DispatcherServlet.properties 檔案(開發者不能自定義覆蓋)如下:
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
DispatcherServlet.properties
檔案中指明:
- 把
AcceptHeaderLocaleResolver
作為預設的本地化解析器 - 把
FixedThemeResolver
作為預設的主題解析器 - 把
BeanNameUrlHandlerMapping, RequestMappingHandlerMapping, RouterFunctionMapping
作為預設的處理器對映元件 - 把
HttpRequestHandlerAdapter, SimpleControllerHandlerAdapter, RequestMappingHandlerAdapter, HandlerFunctionAdapter
作為預設的處理器適配元件 - 把
ExceptionHandlerExceptionResolver, ResponseStatusExceptionResolver, DefaultHandlerExceptionResolver
作為預設的處理器異常解析器 - 把
DefaultRequestToViewNameTranslator
作為預設的檢視查詢處理器 - 把
InternalResourceViewResolver
作為預設的檢視解析器 - 把
SessionFlashMapManager
作為預設的記憶體暫存 session 資料管理器
3.6.1.2. DispatcherServlet.initHandlerAdapters
DispatcherServlet.initHandlerAdapters
public class DispatcherServlet extends FrameworkServlet {
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
// 預設是探測所有的HandlerAdapter,包括父上下文
if (this.detectAllHandlerAdapters) {
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
else {
// 否則直接獲取bean
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {}
}
// 如果以上兩種都沒有定義,則獲取預設的處理策略
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
}
}
}
3.6.1.3. DispatcherServlet.initHandlerExceptionResolvers
DispatcherServlet.initHandlerExceptionResolvers
public class DispatcherServlet extends FrameworkServlet {
private void initHandlerExceptionResolvers(ApplicationContext context) {
this.handlerExceptionResolvers = null;
// 預設是探測所有的HandlerExceptionResolver,包括父上下文
if (this.detectAllHandlerExceptionResolvers) {
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
}
}
else {
// 否則直接獲取bean
try {
HandlerExceptionResolver her =
context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
this.handlerExceptionResolvers = Collections.singletonList(her);
}
catch (NoSuchBeanDefinitionException ex) {}
}
// 如果以上兩種都沒有定義,則獲取預設的處理策略
if (this.handlerExceptionResolvers == null) {
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
}
}
}
3.6.1.4. DispatcherServlet.initRequestToViewNameTranslator
DispatcherServlet.initRequestToViewNameTranslator
public class DispatcherServlet extends FrameworkServlet {
private void initRequestToViewNameTranslator(ApplicationContext context) {
try {
// 獲取bean
this.viewNameTranslator =
context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
}
catch (NoSuchBeanDefinitionException ex) {
// 如果沒有定義bean,則獲取預設的處理策略
this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class);
}
}
}
3.6.1.5. DispatcherServlet.initViewResolvers
DispatcherServlet.initViewResolvers
public class DispatcherServlet extends FrameworkServlet {
private void initViewResolvers(ApplicationContext context) {
this.viewResolvers = null;
// 預設是探測所有的ViewResolver,包括父上下文
if (this.detectAllViewResolvers) {
Map<String, ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.viewResolvers = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.viewResolvers);
}
}
else {
// 否則直接獲取bean
try {
ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
this.viewResolvers = Collections.singletonList(vr);
}
catch (NoSuchBeanDefinitionException ex) {}
}
// 如果以上兩種都沒有定義,則獲取預設的處理策略
if (this.viewResolvers == null) {
this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
}
}
}
3.6.2. DispatcherServlet.doService
剛剛解析完了 DispatcherServlet.onRefresh
,現在來看看 DispatcherServlet.doService
public class DispatcherServlet extends FrameworkServlet {
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ... 程式碼省略
// 給請求物件新增一些上下文資料
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
// ... 程式碼省略
try {
doDispatch(request, response);
}
finally {
// ... 程式碼省略
}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
// 處理器鏈
HandlerExecutionChain mappedHandler = null;
// 是Multipart檔案上傳
boolean multipartRequestParsed = false;
// 非同步處理管理器
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 檢測Multipart檔案上傳
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 獲取處理器,從handlerMappings中查詢符合請求的處理器
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 未找到處理器,404
noHandlerFound(processedRequest, response);
return;
}
// 獲取處理器介面卡,從handlerAdapters中查詢符合處理器的介面卡
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
// 如果是GET或HEAD請求,檢查Last-Modified
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 前置處理,呼叫處理器的preHandle方法,如果有一個不成功,返回
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 呼叫處理器
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// ... 程式碼省略
// 如果沒有檢視名字,新增預設的檢視名
applyDefaultViewName(processedRequest, mv);
// 後置處理,呼叫處理器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
// ... 程式碼省略
}
catch (Throwable err) {
// ... 程式碼省略
}
// 處理handler返回的結果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
// ... 程式碼省略
}
catch (Throwable err) {
// ... 程式碼省略
}
finally {
// ... 程式碼省略
}
}
// 處理handler返回的結果
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
// ... 程式碼省略,如果有異常,呼叫handlerExceptionResolvers處理
}
if (mv != null && !mv.wasCleared()) {
// 渲染檢視
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
// ... 程式碼省略
}
// 渲染檢視
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// ... 程式碼省略
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 呼叫viewResolvers來解析檢視
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
// ... 程式碼省略
}
else {
// ... 程式碼省略
}
// ... 程式碼省略
try {
if (mv.getStatus() != null) {
// 設定http狀態碼
response.setStatus(mv.getStatus().value());
}
// 真實渲染
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
// ... 程式碼省略
}
}
}
3.6.3. 需要後面再解析的幾個點
DispatcherServlet
這個類的解析基本上就差不多了,但還有幾點沒有解析:
- FrameworkServlet(L702):
ConfigurableWebApplicationContext.refresh
- DispatcherServlet(L514):
ApplicationContext.getBean
- DispatcherServlet.properties 檔案中定義的策略處理
- DispatcherServlet(L1393):
View.render
這幾點,我們後面再來解析。
4. ContextLoaderListener
先來看看 ContextLoaderListener
的繼承關係:
- ContextLoader
- ContextLoaderListener
ContextLoaderListener 比較簡單,只有兩個監聽事件方法
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
// ContextLoader.initWebApplicationContext
initWebApplicationContext(event.getServletContext());
}
@Override
public void contextDestroyed(ServletContextEvent event) {
// ContextLoader.closeWebApplicationContext
closeWebApplicationContext(event.getServletContext());
// 銷燬上下文中以"org.springframework."開頭的可銷燬bean
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
ContextLoader 的靜態初始化
public class ContextLoader {
static {
try {
// 從ContextLoader.properties檔案中載入預設的策略
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}
}
ContextLoader.properties 檔案的內容如下:
# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
ContextLoader.properties
檔案中指明使用 XmlWebApplicationContext
作為預設的 Web 應用上下文環境
再來看看 ContextLoader 的 initWebApplicationContext
和 closeWebApplicationContext
4.1. ContextLoaderListener.initWebApplicationContext
ContextLoaderListener.initWebApplicationContext
public class ContextLoader {
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// ... 程式碼省略
try {
// 如果沒有上下文物件,則建立一個新的上下文
// 並呼叫 configureAndRefreshWebApplicationContext(cwac) 配置並重新整理新的上下文
// 預設使用 XmlWebApplicationContext(基於XML載入)作為應用上下文
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
// 未啟用
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
// 配置並重新整理應用上下文
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
// 把上下文註冊到ServletContext中
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
// ... 程式碼省略
return this.context;
}
catch (RuntimeException | Error ex) {
// ... 程式碼省略
}
}
}
ContextLoader.configureAndRefreshWebApplicationContext
與 FrameworkServlet.configureAndRefreshWebApplicationContext
的處理基本上一致。
也就是說,當容器啟動(如 Tomcat、Jetty、Undertow 等)時,Spring 框架會自動進行初始化。
4.2. ContextLoaderListener.closeWebApplicationContext
ContextLoaderListener.closeWebApplicationContext
public class ContextLoader {
public void closeWebApplicationContext(ServletContext servletContext) {
try {
if (this.context instanceof ConfigurableWebApplicationContext) {
// 呼叫上下文物件的close方法
((ConfigurableWebApplicationContext) this.context).close();
}
}
finally {
// ... 程式碼省略
}
}
}
5. 綜述
DispatcherServlet.init
與 ContextLoaderListener.contextInitialized
都會進行應用上下文的初始化,主要過程是:
- 初始化 Web 應用上下文,預設使用
XmlWebApplicationContext
(基於 XML 載入)作為應用上下文,並呼叫refresh
方法 - 例項化由
globalInitializerClasses
和contextInitializerClasses
定義的類 - 例項化 WebMVC 必要的元件:
MultipartResolver
,LocaleResolver
,ThemeResolver
,HandlerMapping
,HandlerAdapter
,HandlerExceptionResolver
,RequestToViewNameTranslator
,ViewResolver
,FlashMapManager
每個請求都會進入到 DispatcherServlet.service
,其主要過程是:
- 初始化請求物件,以便應用後續處理
- 處理 Multipart 檔案上傳,獲取處理器處理當前請求
- 如果當前請求處理髮生異常,進行異常處理
- 進行檢視渲染
6. 未完
到這裡為止,分析僅僅止於 DispatcherServlet
與 ContextLoaderListener
兩個類,下一篇將深入其他類,繼續探索。
ConfigurableWebApplicationContext.refresh
重新整理上下文ApplicationContext.getBean
從上下文中獲取 beanDispatcherServlet.properties
檔案中定義的策略處理ContextLoader.properties
檔案中定義的策略處理View.render
檢視渲染
後續
更多部落格,檢視 https://github.com/senntyou/blogs
版權宣告:自由轉載-非商用-非衍生-保持署名(創意共享 3.0 許可證)