SpringMVC的處理器適配器-HandlerAdapter的用法及說明
SpringMVC處理器適配器HandlerAdapter用法
如題:
今天看spring源碼解析這本書時,看到了這個地方,對于不同HandlerAdapter的使用場景有的困惑,主要還是沒見過,因為大多數面向的Controller類型的HandlerAdapter;
HandlerAdapter目前最常見的主要為
1、SimpleControllerHandlerAdapter、HttpRequestHandlerAdapter 、AnnotationMethodHandlerAdapter (已過時),這三個是默認的;如果沒有指明新的適配器時,會從這三個中間選擇;
2、SimpleServletHandlerAdapter目前很少用到,也不是默認的適配器;
3、RequestMappingHandlerAdapter這個應該是目前springMVC主要采用的,針對方法級的映射匹配處理。
由HandlerAdapter的實現類可知:映射匹配的處理器handler包括三個類型Controller、HttpRequestHandler、Servlet,但后兩個的應用場景與使用方式我卻沒有見到過,經過一番查找,特做一個總結以供以后參考:
1、SimpleControllerHandlerAdapter主要是針對實現Controller接口的handler進行適配,配置方式在書中可見;
2、SimpleServletHandlerAdapter主要是針對實現Servlet接口的handler進行適配,配置方式跟普通的Controller類似,如下所示:
spring配置文件中bean的配置如下:
/** 采用BeanNameUrlHandlerMapping類進行路徑映射匹配**/ <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" /> <bean name="/demo.do" class="com.demo.DemoServlet"/>
對應的Bean實現類為
public class DemoServlet extends HttpServlet{
.......
}3、HttpRequestHandlerAdapter 主要是針對實現HttpRequestHandler接口的handler進行適配;
HTTP請求處理器適配器僅僅支持對HTTP請求處理器的適配。它簡單的將HTTP請求對象和響應對象傳遞給HTTP請求處理器的實現,它并不需要返回值。它主要應用在基于HTTP的遠程調用的實現上。
配置應該與上述相同,從SpringMvc自帶的實現類來說,主要用于轉發(fā)或者靜態(tài)資源的加載,對應的實現類為DefaultServletHttpRequestHandler和ResourceHttpRequestHandler
淺談HandlerAdapter
HandlerAdapter顧名思義就是一個適配器,(肯定也采用了適配器模式這里不做過多的解釋),那么它的主要作用是什么呢?
HandlerMapping存儲了所有都請求映射,請求過來找到相應的請求映射后,返回給我們一個Handler
看這里的代碼:
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
Iterator var2 = this.handlerMappings.iterator();
while(var2.hasNext()) {
HandlerMapping mapping = (HandlerMapping)var2.next();
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}這里是DispacherServlet的核心控制方法。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}我們拿到Handler之后呢?
接著代碼往下走我們可以看到這樣一行代碼
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
這一步就是講我們的Handler進行一個HandlerAdapter的適配解析,我們繼續(xù)代碼跟進
public interface HandlerAdapter {
//如果當前支持當前handler就執(zhí)行handler方法
boolean supports(Object handler);
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
/** @deprecated */
@Deprecated
long getLastModified(HttpServletRequest request, Object handler);
}我們知道它采用的適配器模式
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
Iterator var2 = this.handlerAdapters.iterator();
while(var2.hasNext()) {
HandlerAdapter adapter = (HandlerAdapter)var2.next();
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");
}DispacherServlet中這個方法,就是一個迭代器,給handler找一個適配器,那這一步到底是要做什么呢?

在HandlerAdapter中有四個實現類,分別處理不同Handler我們看一下我們最常用的HttpRequestHandlerAdapter
public boolean supports(Object handler) {
return handler instanceof HttpRequestHandler;
}里面就是去判斷了,當前handler是不是HttpRequestHandler類型,其實在我們確認號類型的時候,handler經過RequestMappingHandlerMapping?,返回的就已經是這個類型的了。
找到適配器之后啊,就是開始參數解析了
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}核心在這里,利用的HandlerAdapter,也就是剛才找到的適配器,和目標方法傳進去,執(zhí)行目標方法。?
執(zhí)行完上面代碼,就是判斷了一下是不是get請求,HEAD這個不是由我們 去處理的。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
上面這行代碼去執(zhí)行的目標方法。
到這里基本是完成了HandlerAdapter的一個準備工作,找到適配的HandlerAdapter之后,還要進行相應的參數解析啊。
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
springboot項目打包的可執(zhí)行jar運行報錯問題及解決
本文介紹了SpringBoot項目打包成可執(zhí)行jar文件后無法使用java-jar命令啟動的問題及解決方法,主要是需要在pom.xml文件中添加spring-boot-maven-plugin插件,以便生成正確的MANIFEST.MF文件2026-04-04
SpringBoot Admin與Prometheus集成監(jiān)控
本文深入探討Spring Boot Admin與Prometheus的集成監(jiān)控方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2026-01-01
使用@PathVariable時候無法將參數映射到變量中的解決
這篇文章主要介紹了使用@PathVariable時候無法將參數映射到變量中的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
Spring?Security?OAuth?Client配置加載源碼解析
這篇文章主要為大家介紹了Spring?Security?OAuth?Client配置加載源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07

