經過前面SpringAOP源碼深度解析,SpringIOC源碼深度解析加上本文的SpringMVC的源碼閱讀,我從中收穫不少,學習了各類設計模式,各類抽象思想,以及各類底層原理,好比動態代理,反射等等,雖然前前先後後大概花了一個多月,可是我不後悔,並不以爲是浪費時間。php
本文比較長,我花了三天的時間完成本文,可是仍是水平有限,不可能面面俱到,當中也可能會有錯的,還請讀者指出,一塊兒交流一塊兒進步。html
本文采用的源碼版本是5.2.x,一樣,爲了能收穫更多,還請讀者打開Spring的源碼工程進行跟進。前端
爲何要先了解Servlet的知識呢,由於後面你會看到咱們所熟悉的SpringMVC其實也是一個Servlet,只是它封裝了不少的東西並和Spring進行了整合,後面咱們進行的源碼分析就是圍繞着Servlet的生命週期進行的,因此有必要了解一下Servlet相關的知識。java
全稱Java Servlet,是用Java編寫的服務器端程序。其主要功能在於 交互式地瀏覽和修改數據,生成動態Web內容。Servlet運行於支持 Java應用的服務器中。從原理上講,Servlet能夠響應任何類型的請求, 但絕大多數狀況下Servlet只用來擴展基於HTTP協議的Web服務器。ios
下面經過一張時序圖來理解Servlet的工做流程程序員
從上面的時序圖總結以下:web
打開Servlet源碼發現Servlet接口有幾個方法:spring
public interface Servlet {
//Servlet建立時會調用init方法進行初始化
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
// 每次有新的請求來時都會調用
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
// 請求結束時調用
void destroy();
}
複製代碼
寫一個AddUserServlet類繼承自HttpServlet(爲何要繼承這個類後面有說明)數據庫
public class AddUserServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain;charset=utf8");
response.getWriter().write("添加成功");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
複製代碼
webapp/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" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<servlet>
<description></description>
<display-name>AddUserServlet</display-name>
<servlet-name>AddUserServlet</servlet-name>
<servlet-class>com.sjc.springmvc.servlet.AddUserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AddUserServlet</servlet-name>
<url-pattern>/AddUserServlet</url-pattern>
</servlet-mapping>
</web-app>
複製代碼
將程序部署到Tomcat就能夠訪問了(具體不會的請讀者查相關資料)
有同窗可能會產生疑惑:我都沒有看到main方法,怎麼就能夠訪問了呢?
回答以下:
Servlet其實是tomcat容器生成的,調用init方法能夠初始化。他有別於 普通java的執行過程,普通java須要main方法;可是web項目因爲是服務器控 制的建立和銷燬,因此servlet的訪問也須要tomcat來控制。經過tomcat訪問 servlet的機制是經過使用http協議的URL來訪問,因此servlet的配置完想要訪 問,必須經過URL來訪問,因此沒有main方法。
也就是咱們常說的web層
MVC是模型(model)、視圖(view)、控制器(controller)的縮寫,是一種用於設計編寫web應用程序表現層的模式
MVC設計模式的三大角色:
Model(模型):
模型包含業務模型和數據模型,數據模型用於封裝數據,業務模型用於處理業務。
View(視圖): 一般指的是咱們的jsp或者html。做用通常就是展現數據的。
一般視圖是依據數據模型建立的。
Controller(控制器):
是應用程序中處理用戶交互的部分。做用通常就是處理程序邏輯的。
理解SpringMVC,只要你理解了下面介紹的六大組件基本上就能夠了。後面的源碼分析咱們也是圍繞着這六大組件來的。
SpringMVC流程以下圖所示:
總結以下:
用戶請求到達前端控制器,它就至關於MVC中的C,DispatcherServlet是整個流程控制的中心,由它調用其餘組件處理用戶的請求,DispatcherServlet的存在下降了組件之間的耦合性
Handler:處理器
Handler是繼DispatcherServlet前端控制器的後端控制器,在DispatcherServlet的控制下Handler對具體的用戶請求進行處理
因爲Handler涉及到具體的用戶業務請求,因此通常狀況下須要程序員根據業務需求開發Handler
經常使用的有好比Controller,HttpRequestHandler,Servlet、@RequestMapping等等,因此說處理器實際上是一個寬泛的概念
View:視圖
SpringMVC框架提供了不少的view視圖類型的支持,包括:jstlview、freemarkerview、pdfview等。咱們最多見的視圖就是jsp。固然,如今不多用jsp了,如今大部分都是先後端分離了,因此後面源碼分析咱們會忽略視圖
HandlerMapping: 處理器映射器
HandlerMapping負責根據用戶請求找到Handler即處理器,SpringMVC提供了不一樣的映射器實現不一樣的映射方式好比:配置文件方式(BeanNameUrlHandlerMapping)、實現接口方式和註解方式(RequestMappingHandlerMapping)等。
HandlerAdapter: 處理適配器
SpringMVC經過適配器模式,將不關聯的DispatcherServlet和Handler進行關聯,經過適配器調用具體的Handler實現。
View Resolver:視圖解析器
View Resolver負責將處理結果生成view視圖,View Resolver首先根據邏輯視圖解析成物理視圖名即具體的頁面地址,再生成view視圖對象,最後對view進行渲染,將處理結果經過頁面展現給用戶。
咱們上面說過,分析源碼的時候從Servlet入手,咱們看它的初始化init()。首先看下類結構圖,咱們發現DispatcherServlet這個核心組件就是一個Servlet,回到開頭咱們說的SpringMVC其實也是一個Servlet,只是作的事情比較多而已。
咱們順着這個類關係圖,找到了FrameworkServlet#initServletBean
這裏初始化了spring容器WebApplicationContext
@Override
protected final void initServletBean() throws ServletException {
//...省略若干代碼
// 初始化web環境中的spring容器WebApplicationContext
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
//...省略若干代碼
}
複製代碼
咱們進入到:FrameworkServlet#initWebApplicationContext
咱們找到了兩個分支
configureAndRefreshWebApplicationContext
這個分支會去初始化Spring容器,又會回到咱們SpringIOC容器初始化的那十二步驟,相關的能夠閱讀我以前分析的深度解析SpringIOC
onRefresh
會刷新容器的策略,咱們主要看這一分支
protected WebApplicationContext initWebApplicationContext() {
//...省略若干代碼
// 初始化spring容器
configureAndRefreshWebApplicationContext(cwac);
// 刷新容器中的策略
onRefresh(wac);
//...省略若干代碼
}
複製代碼
咱們根據onRefresh,發現最終會進入到DispatcherServlet#onRefresh
protected void onRefresh(ApplicationContext context) {
// 初始化策略容器
initStrategies(context);
}
複製代碼
咱們進入到DispatcherServlet#initStrategies
這裏會初始各類解析器,好比咱們比較關心的處理器映射器,處理器適配器,至於爲啥要在這裏作初始化呢?SpringMVC爲了擴展性,使用策略模式,將這些映射器適配器交給了配置文件,這樣若是要再新增一個處理器就不須要改代碼了,符合「對修改關閉,對擴展開放」的設計原則,這裏的初始化也是爲了策略模式作準備這個也是咱們學習源碼學習到的知識,之後能夠運用到咱們實際的項目中。
好比下面的xml配置文件:
initStrategies就是從SpringIOC容器中獲取到這些Bean,而後放入Map中來進行初始化的。
<beans>
<!-- Handler處理器類的配置 -->
<!-- 經過bean標籤,創建beanname和bean的映射關係 -->
<bean name="/queryUser2" class="com.sjc.springmvc.handler.QueryUserHandler"></bean>
<bean name="/addUser2" class="com.sjc.springmvc.handler.AddUserHandler"></bean>
<!-- HandlerMapping配置 -->
<bean class="com.sjc.springmvc.handlermapping.BeanNameUrlHandlerMapping" init-method="init"></bean>
<!-- HandlerAdapter配置 -->
<bean class="com.sjc.springmvc.handleradapter.HttpRequestHandlerAdapter"></bean>
</beans>
複製代碼
讀者感興趣的話能夠當作爲一個分支進行驗證。
protected void initStrategies(ApplicationContext context) {
// 初始化多部件解析器
initMultipartResolver(context);
// 初始化國際化解析器
initLocaleResolver(context);
// 初始化主題解析器
initThemeResolver(context);
// 初始化處理器映射器
initHandlerMappings(context);
// 初始化處理器適配器
initHandlerAdapters(context);
// 初始化異常解析器
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
// 初始會視圖解析器
initViewResolvers(context);
initFlashMapManager(context);
}
複製代碼
在介紹HandlerMapping找到Handler的過程前,咱們先來看看,RequestMappingHandlerMapping的初始化過程發生了什麼。我這裏先給個結論:
咱們最終的目的就是經過url找到Handler(HandlerMethod)
咱們進入到RequestMappingHandlerMapping,看到其中只有這樣的方法:
@Override
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentNegotiationManager(getContentNegotiationManager());
// 調用父類AbstractHandlerMethodMapping的afterPropertiesSet方法
super.afterPropertiesSet();
}
複製代碼
咱們眼前一亮,它爲咱們提供了研究RequestMappingHandlerMapping初始化的入口,爲何這麼說呢?咱們知道SpringIOC提供了兩種初始化方式: 第一種、就是在配置文件中中指定init-method方法,這種在我前面分析SpringIOC的文章能夠看到**深刻解析SpringIOC; **第二種、就是子類實現InitializingBean接口。這個接口有一個方法:
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
複製代碼
這樣在初始化Bean的時候會調用afterPropertiesSet()方法。
這個流程咱們在SpringIOC源碼哪裏能夠看見呢?
咱們進入AbstractAutowireCapableBeanFactory#invokeInitMethods
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable {
//判斷該bean是否實現了實現了InitializingBean接口,若是實現了InitializingBean接口,則只掉調用bean的afterPropertiesSet方法
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
//直接調用afterPropertiesSet
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//直接調用afterPropertiesSet
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
//判斷是否指定了init-method方法,若是指定了init-method方法,則再調用制定的init-method
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
//進一步查看該方法的源碼,能夠發現init-method方法中指定的方法是經過反射實現
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
複製代碼
這兩種方式哪種先調用呢?
看源碼咱們發現實現了InitializingBean接口的類在Bean進行初始化的時候先被調用,而後調用init-method指定的方法;
哪種方式的效率高呢?
固然是實現了InitializingBean接口的類方式,由於調用init-method指定的方法是經過反射實現的;可是經過映射文件方式消除了對spring的依賴
好了別跑遠了,咱們接着看RequestMappingHandlerMapping#afterPropertiesSet
裏面會調用父類AbstractHandlerMethodMapping#afterPropertiesSet
public void afterPropertiesSet() {
// 初始化處理器方法對象
initHandlerMethods();
}
複製代碼
接着進入:AbstractHandlerMethodMapping#initHandlerMethods
protected void initHandlerMethods() {
// 獲取當前spring容器的全部bean的name,並遍歷
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// 處理候選的Bean
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
複製代碼
咱們關心的是processCandidateBean
進入:AbstractHandlerMethodMapping#processCandidateBean
這裏面主要作三件事:
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
// 根據bean的名稱,從當前spring容器中獲取對應的Bean的Type
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
// 若是是Handler,則須要查找HandlerMethod(若是帶有@Controller或者@RequestMapping則是Handler對象)
if (beanType != null && isHandler(beanType)) {
// 重要入口
// 從Controller或者RequestMapping註解的Bean中,找到全部的HandlerMethod對象,並進行存儲
detectHandlerMethods(beanName);
}
}
複製代碼
咱們進入AbstractHandlerMethodMapping#detectHandlerMethods
這個方法比較複雜,主要用的lambda表達式太多,主要作這幾件事:
protected void detectHandlerMethods(Object handler) {
// 獲取處理器類型
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
// 若是該類是經過cglib代理的代理類,則獲取其父類類型,不然的話,直接返回該類
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 存儲Method方法和RequestMapping註解信息的映射關係(重點)
// 該映射關係會解析成咱們須要的其餘兩個映射關係
// key是Controller類中的Method對象,value是RequestMappingInfo對象
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> { // 此處是設置回調函數
try {
// 獲取bean上面和method上面的RequestMapping註解信息,封裝到RequestMappingInfo對象中
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
// 註冊HandlerMethod和RequestMappingInfo對象的關係
// 註冊請求URL和RequestMappingInfo對象的關係
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
複製代碼
RequestMappingHandlerMapping初始化分析到此結束,在深刻下去就會沒完沒了。
一樣順着下面的流程圖咱們找到實現Servlet#service()方法的類HttpServlet
咱們來到:HttpServlet#service:
能夠看到這裏面將ServletRequest轉成HttpServletRequest, ServletResponse轉成HttpServletResponse,這樣就能夠作更多的事情了。
@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);
}
複製代碼
進入到HttpServlet#service(request, response):
全部的Http請求都會通過這裏,可是咱們找半天沒發現service實現代碼在哪裏,咱們看到HttpServlet是一個抽象類,通常類被設計成抽象類有兩個因素:
那這個HttpServlet爲何要設計成抽象類呢?別急,咱們看下類的註釋文檔:
翻譯起來大概的意思就是我這個類不知道你子類是什麼處理請求的,我不會幫你處理的,我這裏定義好了各類請求,請你務必實現其中的某一個,否則我就給你返回錯誤。咱們看到這裏就是用了抽象模板方法的設計模式:父類把其餘的邏輯處理完,把不肯定的業務邏輯抽象成一個抽象方法,交給子類去實現。
/** * * Provides an abstract class to be subclassed to create * an HTTP servlet suitable for a Web site. A subclass of * <code>HttpServlet</code> must override at least * one method, usually one of these: * * <ul> * <li> <code>doGet</code>, if the servlet supports HTTP GET requests * <li> <code>doPost</code>, for HTTP POST requests * <li> <code>doPut</code>, for HTTP PUT requests * <li> <code>doDelete</code>, for HTTP DELETE requests * <li> <code>init</code> and <code>destroy</code>, * to manage resources that are held for the life of the servlet * <li> <code>getServletInfo</code>, which the servlet uses to * provide information about itself * </ul> * / 複製代碼
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} 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 {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
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);
}
}
複製代碼
咱們順藤摸瓜,找到實現HttpServlet的子類,看看哪一個子類實現了service()方法,咱們最終看到了DispatcherServlet實現了這個service()方法。
這裏咱們千呼萬喚的doDispatch終於出來了,這個doDispatch作了它擅長的事情,就是請求的分發,咱們得慢慢品,細細品這個方法。
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
//...省略掉無數代碼
// 處理請求分發(作調度)
doDispatch(request, response);
}
複製代碼
咱們進入到:DispatcherServlet#doDispatch
咱們再回顧一下SpringMVC的處理流程:
這個方法就是幹這件事情的,一張圖勝似千言萬語。你品,你細品
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 處理文件上傳的request請求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// 經過處理器映射器HandlerMapping,獲取handler處理器執行鏈,該執行鏈封裝了處理器和對應該處理器的攔截器(可能有多個)
// 須要注意的是@Controller註解的類,它不是咱們這裏要查找的處理器,咱們要查找的處理器是@RequestMapping對應的方法,這個方法會封裝到HandlerMethod類中
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 經過找到的handler處理器,去匹配合適的處理器適配器HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
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;
}
}
// 執行攔截器(interceptor)的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 經過處理器適配器,真正調用處理器方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 設置默認視圖名稱
applyDefaultViewName(processedRequest, mv);
// 執行攔截器(interceptor)的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 處理調度結果(也就是ModelAndView對象)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
// 執行攔截器(interceptor)的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
// 執行攔截器(interceptor)的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
複製代碼
經過HandlerMapping找到Handler的過程
咱們先來看下請求經過HandlerMapping找到Handler的過程:
咱們進入到DispatcherServlet#getHandler
不出咱們所料,這裏就遍歷了咱們初始化階段存儲的handlerMappings集合,返回HandlerExecutionChain。這裏使用到了策略模式:
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// 遍歷全部的處理器映射器
for (HandlerMapping mapping : this.handlerMappings) {
// 經過處理器映射器,查找具體的處理器執行鏈
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
複製代碼
何爲策略模式呢?咱們一塊兒來看:
咱們假設有這樣的需求:咱們出行方式有不少種,好比火車,飛機,自行車,咱們只須要輸入咱們有的錢就能夠智能地匹配出咱們的出行方式
咱們來創建這樣的模型:
定義一個策略類:
// 策略類
public interface TravelStrategy {
//出行方式
void travelWay();
boolean isDone(int type);
}
複製代碼
定義飛機出行方式類:AirStrategy
public class AirStrategy implements TravelStrategy {
@Override
public void travelWay() {
System.out.println("坐飛機");
}
@Override
public boolean isDone(int type) {
if (type <= 1000 && type >500) {
return true;
}
return false;
}
}
複製代碼
定義自行車出行方式類: BicycleStrategy
public class BicycleStrategy implements TravelStrategy {
@Override
public void travelWay() {
System.out.println("自行車");
}
@Override
public boolean isDone(int type) {
if (type <= 20) {
return true;
}
return false;
}
}
複製代碼
定義火車出行方式類:TrainStrategy
public class TrainStrategy implements TravelStrategy {
@Override
public void travelWay() {
System.out.println("坐火車");
}
@Override
public boolean isDone(int type) {
if (type >= 20 && type < 400) {
return true;
}
return false;
}
}
複製代碼
定義一個策略模式環境類(Context)
public class PersonContext {
// 策略類集合
private List<TravelStrategy> strategylist;
public PersonContext() {
this.strategylist = new ArrayList<>();
strategylist.add(new AirStrategy());
strategylist.add(new TrainStrategy());
strategylist.add(new BicycleStrategy());
}
public void travel(int type) {
// 輸入一個數,循環遍歷每一個策略類,進行最優選擇
for (TravelStrategy travelStrategy : strategylist) {
if (travelStrategy.isOK(type)) {
travelStrategy.travelWay();
break;
}
}
}
}
複製代碼
測試類:
public class StrategyTest {
@Test
public void test() {
// 策略環境類
PersonContext person = new PersonContext();
// 坐飛機
person.travel(1500);
// 坐火車
person.travel(100);
// 自行車
person.travel(1);
}
}
複製代碼
輸出:
坐飛機
坐火車
自行車
複製代碼
咱們再來看SpringMVC中的策略模式,首先環境類DispatcherServlet, 初始化各個策略模式的是DispatcherServlet#initStrategies, 遍歷策略選擇最合適的策略的是:
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// 遍歷全部的處理器映射器
for (HandlerMapping mapping : this.handlerMappings) {
// 經過處理器映射器,查找具體的處理器執行鏈
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
複製代碼
策略模式消除了不少的if....else...代碼,經過配置文件方式定義各類策略,是一種可擴展的設計模式。
咱們進入到AbstractHandlerMapping#getHandler
主要作兩件事:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 調用具體的子類去獲取不一樣類型的處理器對象(好比獲取到的@Controller和@RequestMapping註解的處理器是HandlerMethod對象)
Object handler = getHandlerInternal(request);
//...省略若干代碼
// 建立處理器執行鏈
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
//...省略若干代碼
return executionChain;
}
複製代碼
咱們來到實現類AbstractHandlerMethodMapping#getHandlerInternal
咱們看到這裏的handler是HandlerMethod,這個類封裝了controller和Method,
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 獲取查找路徑(部分URL)
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
this.mappingRegistry.acquireReadLock();
try {
// 根據請求查找路徑(部分URL),獲取最合適的HandlerMethod對象
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
// 對HandlerMethod對象包含的的bean屬性進行實例化,再返回HandlerMethod對象
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
複製代碼
咱們進入到AbstractHandlerMethodMapping#lookupHandlerMethod
這裏主要作:
根據請求路徑(URL)去上面咱們分析到的urlLookup集合中獲取匹配到的RequestMappingInfo集合
將RequestMappingInfo對象和HandlerMethod對象進行匹配,將匹配到的信息封裝到Match對象,再將Match對象放入matches集合進行返回。
此時咱們的處理器HandlerMethod已經找到。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// 根據查找路徑(URL)去urlLookup集合中獲取匹配到的RequestMappingInfo集合
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
// 將RequestMappingInfo對象和HandlerMethod對象進行匹配,將匹配到的信息封裝到Match對象,再將Match對象放入matches集合
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
//...省略尋找最優匹配的過程代碼
}
複製代碼
咱們再回到DispatcherServlet#doService 方法中
找到Handler後按照咱們上面的流程圖,接下來就是要找到HandlerAdapter。
咱們看到:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 經過找到的handler處理器,去匹配合適的處理器適配器HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
}
複製代碼
咱們進入到:DispatcherServlet#getHandlerAdapter
這裏無非就是根據咱們初始化過程當中,將配置文件中的HandlerAdapters集合進行遍歷,找到合適的HandlerAdapter。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
// 經過適配器的適配功能,去適配處理器,若是適配成功,則直接將適配器返回
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
複製代碼
這裏用到了適配器模式,咱們來看看HandlerAdapter類:
public interface HandlerAdapter {
// 判斷是否與當前的適配器支持,若是支持返回true,不支持返回false
boolean supports(Object handler);
// 調用handler中的請求處理方法
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
複製代碼
SpringMVC這裏爲啥須要適配器呢?咱們來分析一下,因爲處理器handler有不少,好比Controller,HttpRequestHandler,Servlet等等,從HandlerMapping中獲取到的Handler是一個Object對象,這樣在DispatcherServlet#getHandlerAdapter方法中若是不用適配器模式就可能這樣寫:
private HandlerAdapter getHandlerAdapter(Object handler) {
if (handler instanceof HttpRequestHandler) {
return new HttpRequestHandlerAdapter();
} else if(handler instanceof Controller) {
return new Controller
}
// else if.....
return null;
}
複製代碼
若是再新增一個handler就得改代碼,不符合「對修改關閉,對擴展開放」的設計原則。
SpringMVC這裏就給每一個handler對應一個相應的適配器,好比針對Controller實現的handler對應的是SimpleControllerHandlerAdapter適配器,由SimpleControllerHandlerAdapter適配器調用Controller處理器處理相關邏輯,每一個handler的處理邏輯不同,之後再新增一個handler的時候,只用新增相應的適配器就能夠了,這完美的解決了這個問題。但願讀者能細品其中的設計思想。
咱們繼續看DispatcherServlet中的其餘方法:
找到HandlerAdapter後,主要作如下幾件事:
SpringMVC的攔截器功能源碼就在此步驟完成,若是讀者對此感興趣能夠以此爲分支進行深刻研究,至此SpringMVC源碼分析告一段落。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//...省略若干代碼
// 經過找到的handler處理器,去匹配合適的處理器適配器HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 執行攔截器(interceptor)的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 經過處理器適配器,真正調用處理器方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 設置默認視圖名稱
applyDefaultViewName(processedRequest, mv);
// 執行攔截器(interceptor)的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 處理調度結果(也就是ModelAndView對象)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
// 執行攔截器(interceptor)的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
複製代碼