我們到底能走多遠系列38
扯淡:
馬航的事,挺震驚的。還是多多珍惜身邊的人吧。
主題:
Spring mvc 作為表現層的框架,整個流程是比較好理解的,畢竟我們做web開發的,最早也經常接觸的就是一個request進一個response出的http請求。
- URL到框架的映射。
- http請求參數綁定
- http響應的生成和輸
下面是網上對spring mvc的整個流程的執行順序圖:
這次慢慢看源碼,所以先來看看第一步,就是根據url找到指定的controller來執行的過程。其實就是一個url路由功能實現。
原理可以理解成有一個map存着key是url,值是controller的map,然后來一個url,找到對應的controller就好了。

public interface HandlerMapping { String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping" ; String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern"; String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping" ; String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables"; String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables"; String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes"; /** * Return a handler and any interceptors for this request. The choice may be made * on request URL, session state, or any factor the implementing class chooses. * <p>The returned HandlerExecutionChain contains a handler Object, rather than * even a tag interface, so that handlers are not constrained in any way. * For example, a HandlerAdapter could be written to allow another framework's * handler objects to be used. * <p>Returns <code> null</code> if no match was found. This is not an error. * The DispatcherServlet will query all registered HandlerMapping beans to find * a match, and only decide there is an error if none can find a handler. * @param request current HTTP request * @return a HandlerExecutionChain instance containing handler object and * any interceptors, or <code>null</code> if no mapping found * @throws Exception if there is an internal error */ HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }
HandlerMapping實現其實維護了一個HashMap<String, Object>,其中key是http請求的path信息,value可以是一個字符串,或者是一個處理請求的HandlerExecutionChain
在AbstractUrlHandlerMapping源碼:
private final Map <String, Object> handlerMap = new LinkedHashMap< String, Object >();
DispatcherServlet在選用HandlerMapping的過程中,將根據我們所指定的一系列 HandlerMapping的優先級進行排序,然后優先使用優先級在前的HandlerMapping。
如果當前的HandlerMapping能夠返 回可用的Handler,DispatcherServlet則使用當前返回的Handler進行Web請求的處理,而不再繼續詢問其他的 HandlerMapping。否則,DispatcherServlet將繼續按照各個HandlerMapping的優先級進行詢問,直到獲取一個可 用的Handler為止。
<bean class= "org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" > <property name= "order" value= "1" /> </bean > <bean id= "urlMapping" class= "org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" > <property name= "mappings"> <value> dealer/login.html=DealerController customer/login.html=CustomerController </value> </property> <property name= "order" value= "0" /> </bean >
ControllerClassNameHandlerMapping的order為 1 SimpleUrlHandlerMapping的order為0 如果各自配置了相同url對用的Controller那么就優先使用SimpleUrlHandlerMapping的。其實這里應該理解為優先使用SimpleUrlHandlerMapping中的HandlerExecutionChain更為准確,關於HandlerExecutionChain后面詳細理解。

/** List of HandlerMappings used by this servlet */ private List<HandlerMapping> handlerMappings; protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { for (HandlerMapping hm : this.handlerMappings) { if (logger .isTraceEnabled()) { logger.trace( "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); } HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } return null ; }
遍歷一遍所有的HandlerMapping,每次檢查有沒有需要的handler,第一個找到的就返回。如此看來這個List<HandlerMapping>應該是有優先級的,餓哦們可以看HandlerMapping的第一個實現類(AbstractHandlerMapping)它就繼承了Ordered來獲得一個優先級的功能。
那么初始化這個List<HandlerMapping>的源碼:
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this .detectAllHandlerMappings ) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils. beansOfTypeIncludingAncestors(context, HandlerMapping.class , true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values()); // We keep HandlerMappings in sorted order. // 也就這個方法啦 OrderComparator.sort(this.handlerMappings); } } else { try { HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME , HandlerMapping.class ); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. if (this .handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger .isDebugEnabled()) { logger.debug( "No HandlerMappings found in servlet '" + getServletName() + "': using default"); } } }
直接看OrderComparator. sort( this. handlerMappings);的實現:
// 繼承Comparator接口 public class OrderComparator implements Comparator<Object> { /** * Shared default instance of OrderComparator. */ public static final OrderComparator INSTANCE = new OrderComparator(); // 實現compare方法 public int compare(Object o1, Object o2) { boolean p1 = (o1 instanceof PriorityOrdered); boolean p2 = (o2 instanceof PriorityOrdered); if (p1 && !p2) { return -1; } else if (p2 && !p1) { return 1; } // Direct evaluation instead of Integer.compareTo to avoid unnecessary object creation. int i1 = getOrder(o1); int i2 = getOrder(o2); // 比較的就是各自的order return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0; } // 結合Order protected int getOrder(Object obj) { return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : Ordered.LOWEST_PRECEDENCE ); } public static void sort(List<?> list) { if (list.size() > 1) { // 最后還是用着方法比較 Collections. sort(list, INSTANCE); } } public static void sort(Object[] array) { if (array.length > 1) { Arrays. sort(array, INSTANCE); } } }
前面提到過,HandlerMapping 中的 getHandler返回的類型是:HandlerExecutionChain
HandlerExecutionChain 其實就是下面兩:
private final Object handler ; private HandlerInterceptor [] interceptors ;
HandlerInterceptor 就是攔截器,而HandlerExecutionChain 又是HandlerMapping 封裝,所以在配置spring 攔截器的時候,我們只要把攔截器定義好然后注入到HandlerMapping 中,所有屬於該map的handler都會使用到這些攔截器了,又根據上面多個mapping的機制,我們可以理解在多個mapping,而且url重復的時候,有可能一些優先級比較低的mapping中的攔截器就不會被執行到,這就是所謂spring沒有完全全局的攔截器的由來。
所以可以說,spring的攔截器是定義在HandlerMapping 層面的,那么這就牽涉到粒度的問題了,前面說的多個HandlerMapping 的情況,還有特定handler的特殊攔截器配置就需要在代碼對請求進行過濾實現了。
HandlerInterceptor 源碼:
public interface HandlerInterceptor { // Called before the handler execution, returns a boolean value, “true” : continue the handler execution chain; “false”, stop the execution chain and return it. boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception; void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception; }
網上解釋一下:
(1 )preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,顧名思義,該方法將在請求處理之前進行調用。SpringMVC 中的Interceptor 是鏈式的調用的,在一個應用中或者說是在一個請求中可以同時存在多個Interceptor 。每個Interceptor 的調用會依據它的聲明順序依次執行,而且最先執行的都是Interceptor 中的preHandle 方法,所以可以在這個方法中進行一些前置初始化操作或者是對當前請求的一個預處理,也可以在這個方法中進行一些判斷來決定請求是否要繼續進行下去。該方法的返回值是布爾值Boolean類型的,當它返回為false 時,表示請求結束,后續的Interceptor 和Controller 都不會再執行;當返回值為true 時就會繼續調用下一個Interceptor 的preHandle 方法,如果已經是最后一個Interceptor 的時候就會是調用當前請求的Controller 方法。
(2 )postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,由preHandle 方法的解釋我們知道這個方法包括后面要說到的afterCompletion 方法都只能是在當前所屬的Interceptor 的preHandle 方法的返回值為true 時才能被調用。postHandle 方法,顧名思義就是在當前請求進行處理之后,也就是Controller 方法調用之后執行,但是它會在DispatcherServlet 進行視圖返回渲染之前被調用,所以我們可以在這個方法中對Controller 處理之后的ModelAndView 對象進行操作。postHandle 方法被調用的方向跟preHandle 是相反的,也就是說先聲明的Interceptor 的postHandle 方法反而會后執行,這和Struts2 里面的Interceptor 的執行過程有點類型。Struts2 里面的Interceptor 的執行過程也是鏈式的,只是在Struts2 里面需要手動調用ActionInvocation 的invoke 方法來觸發對下一個Interceptor 或者是Action 的調用,然后每一個Interceptor 中在invoke 方法調用之前的內容都是按照聲明順序執行的,而invoke 方法之后的內容就是反向的。
(3 )afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,該方法也是需要當前對應的Interceptor 的preHandle 方法的返回值為true 時才會執行。顧名思義,該方法將在整個請求結束之后,也就是在DispatcherServlet 渲染了對應的視圖之后執行。這個方法的主要作用是用於進行資源清理工作的
總結:
1,order的設計值得借鑒
2,閱讀源碼可以升入理解攔截器的靈活使用。
讓我們繼續前行
----------------------------------------------------------------------
努力不一定成功,但不努力肯定不會成功。