8.Spring MVC重要元件概述
這裡說的元件指的是DispatcherServlet中直接初始化的那9個元件;
HandlerMapping
HandlerMapping的作用是根據request找到相應的處理器Handler和Interceptors;
HandlerMapping接口裡面只有一個方法,方法的實現非常靈活,只要使用Request返回HandlerExecutionChain即可;
你可以把HandlerExecutionChain看做是Handler+Interceptors;
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
HandlerMapping需要註冊到Spring MVC的容器裡面才可以使用,註冊也很簡單,只需要在配置檔案裡面配置一個Bean就可以了,Spring MVC會按照型別將它註冊到HandlerMapping中;
可能有多個HandlerMapping可以處理同個url,可以用order標註優先順序,order越小越先使用;
查詢Handler是按順序遍歷所有的HandlerMapping,當找到一個HandlerMapping後立即停止查詢並返回;
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping hm : this.handlerMappings) { ...... HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; //立即返回 } } } return null; }
HandlerAdapter
可以理解為使用Handler的人;它裡面共有三個方法,
supports(Object handler)判斷是否可以使用某個Handler;
handler方法是用來具體使用Handler幹活的;
getLastModified是獲取資源的Last-Modified,Last-Modified是資源最後一次修改的時間;
public interface HandlerAdapter { boolean supports(Object handler); //注意:是Object型別 ModeAndView handle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception; long getLastModified(HttpServletRequest request,Object handler); }
之所以要使用HandlerAdapter是因為Spring MVC中並沒有對Handler做任何限制,Handler可以以任意合理的方式來實現,可以是個類,也可以是個方法,還可以是別的合理的形式;從supports方法的引數是Object型別就可以看出這一點;
//這是Spring MVC自帶的HandlerAdapter
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller); //實現了Controller介面的Handler
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//直接呼叫handleRequest方法
return ((Controller) handler).handleRequest(request, response);
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}
選擇使用哪個HandlerAdapter的過程在getHandlerAdapter方法中,它的邏輯是遍歷所有的Adapter,然後檢查哪個可以處理當前的Handler,找到第一個可以處理Handler的Adapter後就停止查詢並將其返回;既然查詢,那肯定有個順序,這順序也是通過Order屬性來設定的;
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter ha : this.handlerAdapters) {
......
if (ha.supports(handler)) {
return ha;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
HandlerAdapter需要註冊到Spring MVC的容器裡,註冊方法和HandlerMapping一樣,只要配置一個Bean就可以了;Handler是從HandlerMapping裡返回的;
HandlerExceptionResolver
處理異常,根據異常設定ModelAndView,之後再交給render方法進行渲染;render只負責將ModelAndView渲染成頁面,具體ModelAndView從哪裡來的不重要;
從doDispatcher的分析可以知道,HandlerExceptionResolver只是用於解析對請求做處理的過程中產生的異常,而渲染環節產生的異常不歸它管,現在我們知道原因了:它是在render之前工作的,先解析出ModelAndView之後render才去渲染,當然它就不能處理render過程中的異常了;
public interface HandlerExceptionResolver {
//異常--> ModelAndView
@Nullable
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
具體實現上,維護一個異常為key,View為value的Map,解析的時候直接從Map裡獲取View就可以了,如果在Map裡沒有相應的異常可以返回預設的View;我們可以在這個View上顯示一些關於錯誤的細節;
ViewResolver
ViewResolver將String型別的檢視名和Locale解析為View;
public interface ViewResolver {
//檢視名和Locale,不過一般情況下,只需要根據檢視名找到對應的檢視,然後渲染就行了
//locale,是用來達到,不同地區使用不同的View,一般用不到
@Nullable
View resolveViewName(String viewName, Locale locale) throws Exception;
}
ViewResolver的使用需要註冊到Spring MVC的容器中,預設使用的是org.spring.framework.web.servlet.view.InternalResourceViewResolver;
RequestToViewNameTranslator
ViewResolver是根據ViewName查詢View,但有的Handler處理完成之後並沒有設定View也沒有設定viewName,這時就需要從request中獲取viewName,而如何從request中獲取ViewName就是RequestToViewNameTranslator要做的事情;
public interface RequestToViewNameTranslator {
String getViewName(HttpServletRequest request) throws Exception;
}
FlashMapManager
FlashMap主要用在redirect中傳遞引數,而FlashMapManager是用來管理FlashMap的;
public interface FlashMapManager {
//恢復引數,並將恢復過額度和超時的引數從FlashMap中刪除
@Nullable
FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);
//將引數儲存起來
void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);
}
預設實現是SessionFlashMapManager,它是將引數儲存到session中;