一、Servlet Filter與Spring interceptor的執(zhí)行順序
Filter有順序嗎?我們怎么控制filter的執(zhí)行順序。通過Tomcat的代碼分析,servlet在Filter執(zhí)行完成后才調(diào)用,如有多個(gè)filter怎么控制執(zhí)行順序,首先會(huì)想到在web.xml配置某個(gè)參數(shù),例如order之類的,但查找一下一番,servlet并沒有這個(gè)參數(shù)。試試filter Mapping的配置的先后順序,果然有效,原來filter的執(zhí)行順序就考filter mapping在web.xml中的順序。
spring interceptor也是這樣的執(zhí)行順序,不過interceptor多一個(gè)配置參數(shù)order通過他也可以來實(shí)現(xiàn)interceptor的執(zhí)行順序。很多應(yīng)用場景中,執(zhí)行順序還是重要的,比如cache和transaction interceptor的執(zhí)行順序,很顯然cache應(yīng)該在transaction之前,這樣發(fā)現(xiàn)命中了就不用打開事務(wù),如果transaction在前,每次都打開事務(wù)即使cache命中,這是一個(gè)無謂東動(dòng)作。
二、利用springMVC的interceptor實(shí)現(xiàn)頁面性能監(jiān)控(Filter亦可)
調(diào)優(yōu)第一步,找出耗時(shí)比較長的頁面進(jìn)行優(yōu)化。利用interceptor能輕易搞定。interceptor提供了preHandle和postHandle以及afterCompletion三個(gè)方法。preHandle調(diào)用controller具體方法之前調(diào)用,postHandle完成具體方法之后調(diào)用,afterCompletion完成對頁面的render以后調(diào)用,至此整個(gè)頁面渲染完成。也就是說我們在preHandle記錄開始的時(shí)間,在afterCompletion記錄結(jié)束的時(shí)間,就可或者整個(gè)頁面生成的時(shí)間。Spring自帶StopWatch工具類來實(shí)現(xiàn)時(shí)間跟蹤,關(guān)鍵一點(diǎn)interceptor不是線程安全的。我們需要借助threadlocal來實(shí)現(xiàn)線程安全。
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if(usePerformance){ StopWatch stopWatch = new StopWatch(handler.toString()); stopWatchLocal.set(stopWatch); stopWatch.start(handler.toString()); } return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { if(usePerformance){ StopWatch stopWatch = stopWatchLocal.get(); stopWatch.stop(); String currentPath = request.getRequestURI(); String queryString = request.getQueryString(); queryString = queryString == null ? "":"?" + queryString; log.info("access url path:" + currentPath + queryString + " |time:" + stopWatch.getTotalTimeMillis()); stopWatchLocal.set(null); } } |
如果你沒有使用springMVC可以使用filter來完成:
stopWatch.start(); doFilterChain(); stopWatch.stop(); |
三、SpringMVC 攔截器實(shí)現(xiàn)分析
SpringMVC的攔截器不同于Spring的攔截器,SpringMVC具有統(tǒng)一的入口DispatcherServlet,所有的請求都通過DispatcherServlet,所以只需要在DispatcherServlet上做文章即可,DispatcherServlet也沒有代理,同時(shí)SpringMVC管理的Controller也不有代理。哪不難想到我們在執(zhí)行controller之前做某些動(dòng)作,執(zhí)行完畢做某些動(dòng)作,render完成做某些動(dòng)作。SpringMVC的攔截器對應(yīng)提供了三個(gè)preHandle,postHandle,afterCompletion方法。只需在三個(gè)方法內(nèi)寫我們需要的邏輯就行,多了都是廢話,還是代碼實(shí)在。
HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); if (interceptors != null) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; //ha.handle是調(diào)用具體的controller在此之前執(zhí)行preHandle if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) { triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); return; } interceptorIndex = i; } } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); |
完成調(diào)用之后,調(diào)用render(),最后執(zhí)行afterCompletion()。
if (interceptors != null) { for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv); } } } catch (ModelAndViewDefiningException ex) { logger.debug("ModelAndViewDefiningException encountered", ex); mv = ex.getModelAndView(); } catch (Exception ex) { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(processedRequest, response, handler, ex); errorView = (mv != null); } // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { render(mv, processedRequest, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isDebugEnabled()) { logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + "': assuming HandlerAdapter completed request handling"); } } // Trigger after-completion for successful outcome. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); |