Spring MVC適配器模式實踐之HandlerAdapter源碼分析【享學Spring MVC】

  • 2019 年 10 月 10 日
  • 筆記

版權聲明:本文為部落客原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。

本文鏈接:https://blog.csdn.net/f641385712/article/details/89844141

每篇一句

每當你把事情往外推的時候,你也是在把機會往外推

前言

如果說理解了HandlerMapping相當於掌握了Spring MVC的1/3,那麼若你繼續理解了HandlerAdapter(以及它的相關組件),那幾乎可以說你就理解了它剩下的2/3了。

個人誇張劃分,不喜勿噴

HandlerAdapter的作用:因為Spring MVC中的Handler可以是多種/4種形式,但是Servlet需要的處理方法的結構卻是固定的,都是以requestresponse為方法入參,那麼如何讓固定的Servlet處理方法調用靈活的Handler來進行處理呢?這就是HandlerAdapter要做的事情–> 適配。

它的作用是:根據 Handler 來找到支援它的 HandlerAdapter,通過 HandlerAdapter 執行這個 Handler 得到 ModelAndView 對象。

適配器模式簡介

假如你有現在存在一個類的介面方法,但是這個介面不太符合你的預期(方法簽名對應不上),如果要用他就需要在他的源碼上進行一些修改,顯然這個不可行。 這時還有一種方案:你可以做一個適配器,在不修改原來這個介面源碼的情況下,在適配器上對這個介面進行運用,使得適配器符合你的介面規範

其實生活上適配器有大量的應用,最為常見的就是電源適配器吧~

適配器模式(Adapter Pattern):把一個類的介面變換成客戶所期待的另一種介面, Adapter模式使原本因介面不匹配(或者不兼容)而無法在一起工作的兩個類能夠在一起工作。 適配器模式又稱為轉換器模式、變壓器模式、包裝(Wrapper)器模式(把已有的一些類包裝起來,使之能有滿足需要的介面)。

以下情況可以使用適配器模式

  • 你想使用一個已經存在的類,而它的介面不符合你的需求(但你又不能修改器源碼)
  • 你想創建一個可以復用的類,該類可以與其他不相關的類或不可預見的類(即那些介面可能不一定兼容的類)協同工作

HandlerAdapter就是利用適配器模式的一個實現,它在Spring MVC體系中的地位舉足輕重。


HandlerAdapter

中文釋義:Handler的適配器。JavaDoc解釋為:MVC框架SPI,允許核心MVC工作流的參數化。 它必須為每個Handler程式都準備一個對應的適配器來處理請求,有了這個SPI介面,它能夠讓DispatcherServlet無限的擴展:能夠兼容一切的Handler處理器類型。

DispatcherServlet就是通過這個介面來訪問handler的,而不是直接去訪問真正的實際的處理器,這樣做的好處是大大的:

  1. 處理器程式允許是任意的Object
  2. 集成第三方請求處理器的時候,本處程式碼也無需修改

此介面不適用於應用程式開發人員。對於想要開發自己的web工作流的處理程式來說(或者你想進行深度訂製),那就用它吧。

為何需要使用HandlerAdapter適配?

Spring MVCHandlerController介面,HttpRequestHandler,Servlet、@RequestMapping)有四種表現形式,在Handler不確定是什麼方式的時候(可能是方法、也可能是類),適配器這種設計模式就能模糊掉具體的實現,從而就能提供統一訪問介面。

public interface HandlerAdapter {    	// 判斷當前的這個HandlerAdapter  是否 支援給與的handler  	// 因為一般來說:每個適配器只能作用於一種處理器(你總不能把手機適配器拿去用於電腦吧)  	boolean supports(Object handler);    	// 核心方法:利用 Handler 處理請求,然後返回一個ModelAndView  	// DispatcherServlet最終就是調用此方法,來返回一個ModelAndView的~  	@Nullable  	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;  	// 同HttpServlet 的 getLastModified方法  	// Can simply return -1 if there's no support in the handler class.  	long getLastModified(HttpServletRequest request, Object handler);    }
HandlerAdapter.supports()

HandlerAdapter.supports()方法的主要作用在於判斷當前的HandlerAdapter是否能夠支援當前的handler的適配。這裡的handler是由HandlerExecutionChain HandlerMapping.getHandler(HttpServletRequest)方法獲取到的。從這裡可以看出:

  • HandlerMapping的作用主要是根據request請求匹配/映射上能夠處理當前request的handler
  • HandlerAdapter的作用在於將request中的各個屬性,如request param適配為handler能夠處理的形式 – 參數綁定、數據校驗、內容協商…幾乎所有的web層問題都在在這裡完成的。
HandlerAdapter.handle()

執行真正開發者開發的處理方法的地方。Spring MVC自動幫我們完成數據綁定、視圖渲染等等一切周邊工作~

HandlerAdapter.getLastModified()

獲取當前請求的最後更改時間,主要用於供給瀏覽器判斷當前請求是否修改過,從而判斷是否可以直接使用之前快取的結果

它的繼承樹:

從實現類的個數上看是不是很熟悉:4個,剛好對應著我們Handler內置的那四種實現方式~

由簡到繁,逐個分析:

SimpleControllerHandlerAdapter

適配org.springframework.web.servlet.mvc.Controller這種Handler。它是一個非常古老的適配器(幾乎已棄用狀態):

Controller它沒有對參數的自動封裝、校驗等一系列高級功能,但是它保留有對ModelAndView的處理能力,這是區別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  	@Nullable  	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)  			throws Exception {  		return ((Controller) handler).handleRequest(request, response);  	}    	// 此處注意:若處理器實現了`LastModified`介面,那就委託給它了  	// 否則返回-1  表示不要快取~  	@Override  	public long getLastModified(HttpServletRequest request, Object handler) {  		if (handler instanceof LastModified) {  			return ((LastModified) handler).getLastModified(request);  		}  		return -1L;  	}    }

源碼非常之簡單,因為它直接處理的就是源生的HttpServletRequestHttpServletResponse,所以它和Servlet容器是強綁定的。無數據自動封裝、校驗等一系列高級功能,所以實際應用中此種方式很少被使用。

畫外音:Spring5.0後的WebFlux基於Reactive模式是不支援這種Handler的~

HttpRequestHandlerAdapter

適配org.springframework.web.HttpRequestHandler這種Handler。它比Controller方式還源生:

// @since 2.0  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;  	}    	@Override  	public long getLastModified(HttpServletRequest request, Object handler) {  		if (handler instanceof LastModified) {  			return ((LastModified) handler).getLastModified(request);  		}  		return -1L;  	}    }

和上面的唯一不同是:return null。那是因為HttpRequestHandler#handleRequest()它沒有返回值(全靠開發者自己寫response),而Controller最起碼來說還有ModelView自動渲染的能力嘛~

SimpleServletHandlerAdapter

適配javax.servlet.Servlet這種Handler。

public class SimpleServletHandlerAdapter implements HandlerAdapter {    	@Override  	public boolean supports(Object handler) {  		return (handler instanceof Servlet);  	}    	@Override  	@Nullable  	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  		((Servlet) handler).service(request, response);  		return null;  	}    	@Override  	public long getLastModified(HttpServletRequest request, Object handler) {  		return -1;  	}    }

javax.servlet.Servlet的處理方式幾乎同HttpRequestHandler,都是對源生請求進行直接處理。它的特殊之處在於:Spring MVC默認並不向容器註冊這種HandlerAdapter,若需要使用是需要調用者手動給註冊這個BeanServlet這種Handler才能正常使用的~

AbstractHandlerMethodAdapter

從命名中其實就可以看出端倪,它主要是支援到了org.springframework.web.method.HandlerMethod這種處理器,顯然這種處理器也是我們最最最最為常用的

// @since 3.1 @RequestMapping註解是Spring2.5出現的  // 注意:它實現了Ordered介面  public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {    	// 唯一構造函數。傳的false表示:忽略掉supportedMethods這個屬性  	// 默認它的值是GET、POST、HEAD(見WebContentGenerator)  	public AbstractHandlerMethodAdapter() {  		// no restriction of HTTP methods by default  		super(false);  	}    	// 只處理HandlerMethod 類型的處理器。抽象方法supportsInternal默認返回true  	// 是流出的鉤子可以給你自己擴展的  	@Override  	public final boolean supports(Object handler) {  		return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));  	}    	// 抽象方法交給子類handleInternal去實現  	@Override  	@Nullable  	public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  		return handleInternal(request, response, (HandlerMethod) handler);  	}  	...  }

它有個大名鼎鼎的子類:RequestMappingHandlerAdapter。此子類已經把HandlerMethod的實現精確到了@RequestMapping註解方案。這個HandlerAdapter可謂是Spring MVC的精華之所在,只要理解了它以及它的相關組件,就基本可以自信地說自己掌握了Spring MVC

因為這部分內容過於複雜繁多,因此我撰了專文來描述它,詳情請點擊這裡

DispatcherServlet#doDispatch分發流程

主要看看DispatcherServlet是如何使用HandlerAdapter的。

DispatcherServlet:  	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、從Chain里拿出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對於執行流程的通用性起到了非常重要的作用,它能把任何一個處理器(Object)都適配成一個HandlerAdapter,從而可以做統一的流程處理,這也是為何DispatcherServlet它能作為其它web處理框架的分發器的原因(因為它沒有耦合具體的處理器,你完全可以自己去實現)~

總結

本文介紹Spring MVC在處理請求時使用的適配器模式實踐HandlerAdapter,感受到了它對DispatcherServlet的重要性。適配器模式在基礎框架設計中屬常用的一種方式,比如Spring AOP中也有用到,具體請理解我上面說的兩個使用場景。 本文留下一個最最最為重要的子類RequestMappingHandlerAdapter沒有放在本文講述,只因為它過於重要和複雜,請務必出門左拐詳細了解下它,傳送門在此