一、引言
本人在閱讀 SpringMVC
源碼過程中,一直對 HandlerMapping
、HandlerAdapter
有疑惑,一直不理解。心裏知道這裏用的是適配器模式,本人對適配器模式還是知曉的,但這兩個東西就是不理解。最近突然知道了一個知識點,瞬間豁然開朗,至於是哪個知識點,下面慢慢說。
下面這張圖是SpringMVC
的工作流程圖,隨便一搜,應該一大把,這裏只做熟悉用,不會細說。(PS:之前跳槽面試,就有一道筆試題讓我畫SpringMVC的工作流程。。。。)
對上圖做一下簡單總結:
1、請求首先進入DispatcherServlet
, 由DispatcherServlet
從HandlerMappings
中匹配對應的Handler
,此時只是獲取到了對應的Handler
,然後拿着這個Handler
去尋找對應的適配器,即:HandlerAdapter
;
2、拿到對應HandlerAdapter
時,這時候開始調用對應的Handler
方法,即執行我們的Controller
來處理業務邏輯了, 執行完成之後返回一個ModeAndView
;
3、HandlerAdapter
執行完之後,返回一個ModeAndView
,把它交給我們的視圖解析器ViewResolver
,通過視圖名稱查找出對應的視圖然後返回;
4、最後,渲染視圖 返回渲染後的視圖。
二、SpringMVC中定義Controller的方式
在介紹HandlerMapping
、HandlerAdapter
之前,先來說一下SpringMVC
中定義Handler
的方式,本人就是對這個知識點不熟悉,導致對這兩個對象一直不明白。
先說一下最最最最……常用定義Handler
的方式,使用@RequestMapping
註解,下面這段代碼不用介紹吧:
@Controller
public class IndexController {
@RequestMapping("/index")
@ResponseBody
public String sayHello(){
System.out.println("hello ...");
return "hello";
}
}
那大家有沒有用過下面的兩種方式來聲明一個Handler
呢??
實現org.springframework.web.servlet.mvc.Controller
控制器接口,此接口只有一個方法handleRequest()
,用於請求的處理,返回ModelAndView
。 這個接口從第一版SpringMVC
就存在了,所以這個接口是非常古老的接口~~~也是Spring MVC
最早期的實現Handler
的方式
// 關注一下這個包
import org.springframework.web.servlet.mvc.Controller;
@Component("/home")
public class HomeController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
System.out.println("home ...");
return null;
}
// 這地方考慮個問題:怎麼樣實現類似@ResponseBody的功能呢?
// 就是想實現直接向body裏寫數據,而不是返回一個頁面。
// 如果想直接在處理器/控制器裏使用response向客戶端寫回數據,
// 可以通過返回null來告訴 DispatcherServlet我們已經寫出響應了,
// 不需要它進行視圖解析。像下面這樣
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
System.out.println("home ...");
response.getWriter().write("home controller from body");
return null; // 返回null告訴視圖渲染 直接把body裏面的內容輸出瀏覽器即可
}
}
實現org.springframework.web.HttpRequestHandler
接口,HttpRequestHandler
用於處理Http requests
,其類似於一個簡單的Servlet
,只有一個handlerRequest()
方法,其處理邏輯隨子類的實現不同而不同。
// 關注一下這個包
import org.springframework.web.HttpRequestHandler;
@Component("/login")
public class LoginController implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
System.out.println("login...");
response.getWriter().write("login ...");
}
}
再來看一下servlet
的使用,是不是很相似。
@WebServlet("/myServlet")
public class MyServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
super.service(req, resp);
}
}
其實上面這兩種方式第一種使用@RequestMapping
註解一樣,都能定義爲一個Handler
,攔截到對應的請求,並且做出響應。這地方就要牽扯出HandlerMapping
了。
三、何爲HandlerMapping、HandlerAdapter?
從上面的分析,我們知道,Handler
的定義有上面三種(也有可能還有其他方式,比如Servlet),這地方就要引出下面這兩個HandlerMapping:BeanNameUrlHandlerMapping
、RequestMappingHandlerMapping
,當然還有其他HandlerMapping
,下面的斷點圖也能說明這一點。
這裏先說明一下,用註解@RequestMapping
定義的Handler
,用的是RequestMappingHandlerMapping
,上面的其他兩種,用的是BeanNameUrlHandlerMapping
,靜態資源的請求,用的是SimpleUrlHandlerMapping
。
這地方我們可以從 Spring 的角度考慮,Spring 容器在啓動的時候,會去掃描所有的組件,並把它們實例化。當 Spring 容器發現一個方法用@RequestMapping
註解標註的時候,就用RequestMappingHandlerMapping
這個類去實例化,當發現一個類實現了org.springframework.web.servlet.mvc.Controller
這個接口的時候,就用BeanNameUrlHandlerMapping
去實例化,然後將所有請求放在一個Map裏,用請求路徑(比如:/index)和對應的Handler
做映射處理,這樣是不是更好理解。
HandlerMapping的作用:主要是根據request
請求匹配/映射上能夠處理當前request
的Handler
.
下面來看一下如何根據request
來獲取HandlerMapping
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;
}
下面是對/index
請求的斷點調試圖,我們從圖中可以看出,this.handlerMappings
裏面有4個類,有一個爲重複的。循環這個List,判斷這個/index
請求是由哪個Handler
來處理(即查找HandlerMapping
的過程)。
通過循環HandlerMapping
來獲取HandlerExecutionChain
,再次強調,因爲spring
當中存在的Handler
有多種形式,我們處理request
需要通過HandlerExecutionChain
來反射執行Handler
當中的方法,所以不同的Handler
需要new
不同的HandlerExecutionChain
,那麼問題來了HandlerExecutionChain
不知道你的Handler
是什麼類型(因爲HandlerExecutionChain
裏只定義了一個Object handler
屬性,它不知道你的Handler
是什麼類型的),但是HandlerMapping
知道,所以HandlerExecutionChain
的實例化必須依賴HandlerMapping
。
好,講到這終於明白HandlerMapping
的幹嘛的了,至於如何根據/index
去找對應的Handler
和HandlerExecutionChain
,這裏就不做介紹啦。
那上面幾個HandlerMapping
是怎麼來的呢?Spring
容器在初始化的過程中,會調用到initStrategies
中的 initHandlerMappings(context)、initHandlerAdapters(context);
這兩個方法。我們在源碼包的DispatcherServlet.properties
文件下會看見, 它定義了圖片裏的這些屬性。 第一個屬性,就是我們剛看見的HandlerMappings
, 也就是說 HandlerMappings
是SpringMVC
事先定義好的,Spring容器
會幫我們創建。至於第二個屬性,也就是HandlerAdapter
。
介紹完HandlerMapping
之後,下面就要來介紹HandlerAdapter
了。
HandlerAdapter的作用:因爲Spring MVC
中的Handler
可以有多種實現形式,但是Servlet
需要的處理方法的結構卻是固定的,都是以request
和response
作爲方法入參,那麼如何讓固定參數的Servlet
處理方法調用靈活的Handler
來進行處理呢?這就需要HandlerAdapter
來做適配。
爲什麼需要HandlerAdapter?:
前面說過不同的請求會獲取到不同的Handler
,那麼不同的Handler
它是怎麼實現處理不同的請求的呢?我的第一反應是抽象出一個接口,定義一個公共接口,然後讓每個Handler
實現這個接口,我想的沒問題吧,但 Spring
不是這麼做的,爲什麼呢?
再次強調:Spring MVC
的Handler
(Controller接口,HttpRequestHandler,@RequestMapping、Servlet
)有多種表現形式,不同的Handler
,處理請求的方式是不一樣的,註解@RequestMapping
方式使用的是用方法處理請求,而實現Controller
接口和HttpRequestHandler
接口方式使用的是一個類,而適配器模式就能模糊掉具體的實現,從而就能提供統一訪問接口,所以這地方就要使用適配器了。
這樣做的好處有兩個 (1)、處理器程序,也就是Handler
,允許的是任意的Object,只要返回封裝好的HandlerExecutionChain
,具體的Handler
不用管;(2)、集成第三方請求處理器的時候,本處代碼也無需修改,加個適配器就行(PS:這地方可以參考文章最後的模擬程序)
HandlerMapping
的源碼也說明了這一點。HandlerMapping
接口裏面只有一個getHandler()
方法,而且返回類型是HandlerExecutionChain
,用HandlerExecutionChain
裏面定義了一個Object
類型的handler
屬性,並對handler
進行了封裝,在每個請求里加入了攔截器鏈。然後將這個HandlerExecutionChain
裏面的handler
傳給了HandlerAdapter
。
這地方我們可以換個角度,就是萬一處理請求的每個方法不一樣怎麼辦?支持擴展的話,是不是就需要適配器模式了
說了這麼多,是不是終於知道爲什麼需要HandlerAdapter
了。
在得到Handler
之後,就是下面的這行代碼,我們來看一下getHandlerAdapter()
方法
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
protected HandlerAdapter getHandlerAdapter(Object handler)
throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
}
從代碼中能看到,從一個this.handlerAdapters
屬性裏面遍歷了我們的適配器。這個handlerAdapters
哪來的呢? 跟上面的this.HandlerMappings
一樣,在SpringMVC
的配置文件裏面配置的,也就是上圖中的第二個屬性。
實現org.springframework.web.servlet.mvc.Controller
接口形式的處理器,對應的HandlerMapping
是 BeanNameUrlHandlerMapping
,對應的HandlerAdapter
是 HttpRequestHandlerAdapter
實現org.springframework.web.HttpRequestHandler
接口形式的處理器,對應的HandlerMapping
也是 BeanNameUrlHandlerMapping
,對應的HandlerAdapter
也是 HttpRequestHandlerAdapter
。
最後看一下三個適配器中的supports()
和handle()
方法
SimpleControllerHandlerAdapter
SimpleControllerHandlerAdapter
適配org.springframework.web.servlet.mvc.Controller
這種Handler
。源碼非常之簡單,它是一個非常古老的適配器,幾乎已棄用狀態。因爲它直接處理的就是源生的HttpServletRequest
和HttpServletResponse
,所以它和Servlet容器
是強綁定的。無數據自動封裝、校驗等一系列高級功能,所以實際應用中此種方式很少被使用。
// 適配`org.springframework.web.servlet.mvc.Controller`這種Handler
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
// 最終執行邏輯的還是Handler啊~~~~
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
}
HttpRequestHandlerAdapter
HttpRequestHandlerAdapter
適配org.springframework.web.HttpRequestHandler
這種Handler
。它比Controller
方式還源生。
它和上面的唯一不同是:return null
。那是因爲HttpRequestHandler#handleRequest()
它沒有返回值,這就需要全靠開發者自己寫response
,而Controller
最起碼來說還有Model和View
自動渲染的能力。
public class HttpRequestHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
@Override
public ModelAndView handle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
RequestMappingHandlerAdapter
RequestMappingHandlerAdapter
主要是支持到了org.springframework.web.method.HandlerMethod
這種handler
,顯然這種處理器也是我們最最最最爲常用的,它已經把HandlerMethod
的實現精確到了使用@RequestMapping
註解標註的方法。這個類,我們要查看它的父類AbstractHandlerMethodAdapter
。
public class AbstractHandlerMethodAdapter {
// 只處理HandlerMethod 類型的處理器。抽象方法supportsInternal默認返回true
// 是留出的鉤子可以給你自己擴展的
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod
&& supportsInternal((HandlerMethod) handler));
}
@Override
public final ModelAndView handle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
// 抽象方法交給子類handleInternal去實現
return handleInternal(request, response, (HandlerMethod) handler);
}
}
看完之後,再來讀一下DispatcherServlet#doDispatch()
方法的分發流程,看看DispatcherServlet
是如何使用HandlerMapping和HandlerAdapter
。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
throws Exception {
...
//1、根據URL(當然不一定非得是URL)匹配到一個處理器
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 若匹配不到Handler處理器,就404了
noHandlerFound(processedRequest, response);
return;
}
//2、從HandlerExecutionChain裏拿出Handler(注意是Object類型哦~ )然後找到屬於它的適配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
...
//3、執行作用在此Handler上的所有攔截器的Pre方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//4、真正執行handle方法(也就是你自己書寫的邏輯方法),得到一個ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//5、視圖渲染
applyDefaultViewName(processedRequest, mv);
//6、執行攔截器的post方法(可見它是視圖渲染完成了纔會執行的哦~)
mappedHandler.applyPostHandle(processedRequest, response, mv);
...
//7、執行攔截器的afterCompletion方法(不管拋出與否)
}
從執行步驟中可以看到:HandlerAdapter
對於執行流程的通用性起到了非常重要的作用,它能把任何一個Handler(注意是Object類型)
都適配成一個HandlerAdapter
,從而可以做統一的流程處理,這也是爲何DispatcherServlet
它能作爲其它web處理框架
的分發器的原因,因爲它沒有耦合具體的處理器,你完全可以自己去實現。
四、模擬HandlerMapping/HandlerAdapter的適配器模式
如果上面的講法,你還是不懂,下面就用適配器模式模擬一下,這兩個類的具體調用情況,應該會一目瞭然。下面是依賴關係的類圖。其中Controller
代表的就是 HandlerMapping
。具體代碼,可以在文章最後下載。
//多種Controller實現
public interface Controller {
}
// 注意這裏每個實現,都用了不同的方法名, 如果都用一樣的話,就可以放到接口中了
class HttpController implements Controller {
public void doHttpHandler() {
System.out.println("http...");
}
}
class SimpleController implements Controller {
public void doSimplerHandler() {
System.out.println("simple...");
}
}
class AnnotationController implements Controller {
public void doAnnotationHandler() {
System.out.println("annotation...");
}
}
// 定義一個Adapter接口
public interface HandlerAdapter {
public boolean supports(Object handler);
public void handle(Object handler);
}
// 多種適配器類
class SimpleHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((SimpleController) handler).doSimplerHandler();
}
public boolean supports(Object handler) {
return (handler instanceof SimpleController);
}
}
class HttpHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((HttpController) handler).doHttpHandler();
}
public boolean supports(Object handler) {
return (handler instanceof HttpController);
}
}
class AnnotationHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((AnnotationController) handler).doAnnotationHandler();
}
public boolean supports(Object handler) {
return (handler instanceof AnnotationController);
}
}
public class DispatchServlet {
public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>();
public DispatchServlet() {
handlerAdapters.add(new AnnotationHandlerAdapter());
handlerAdapters.add(new HttpHandlerAdapter());
handlerAdapters.add(new SimpleHandlerAdapter());
}
public void doDispatch() {
// 此處模擬SpringMVC從request取handler的對象,
// 適配器可以獲取到希望的Controller
HttpController controller = new HttpController();
// AnnotationController controller = new AnnotationController();
//SimpleController controller = new SimpleController();
// 得到對應適配器
HandlerAdapter adapter = getHandler(controller);
// 通過適配器執行對應的controller對應方法
adapter.handle(controller);
}
public HandlerAdapter getHandler(Controller controller) {
//遍歷:根據得到的controller(handler), 返回對應適配器
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(controller)) {
return adapter;
}
}
return null;
}
public static void main(String[] args) {
new DispatchServlet().doDispatch(); // http...
}
}
注意:Controller
接口的每個實現類,都用了不同的方法名, 這樣的話就需要用到適配器模式了,如果都用一樣的話,就可以放到接口中了,這樣是不是可以理解SpringMVC
中此處的HandlerAdapter
了
五、小結
還是做個小結吧。
SpringMVC
的Handler
有多種實現方式(Controller,HttpRequestHandler,Servlet等),例如繼承Controller
接口的形式,基於註解@Controlle
r控制器方式的,HttpRequestHandler
方式的。由於實現方式不一樣,調用方式就不確定。
,上面的其他兩種,用的是``,這地方要說明一下,靜態資源的請求,用的是
SimpleUrlHandlerMapping`==。
繼承 Controller 方式所使用的HandlerMapping
:BeanNameUrlHandlerMapping
,
繼承 Controller 方式所使用的適配器:HttpRequestHandlerAdapter
、
註解方式@Controller
的HandlerMapping
器:RequestMappingHandlerMapping
註解方式@Controller
適配器:RequestMappingHandlerAdapter
、
這是一一對應的。
如果說的不對的,反饋一下給我啊,謝謝……
代碼下載: