一、Servlet Filter與Spring interceptor的執行順序
Filter有順序嗎?我們怎么控制filter的執行順序。通過Tomcat的代碼分析,servlet在Filter執行完成后才調用,如有多個filter怎么控制執行順序,首先會想到在web.xml配置某個參數,例如order之類的,但查找一下一番,servlet并沒有這個參數。試試filter Mapping的配置的先后順序,果然有效,原來filter的執行順序就考filter mapping在web.xml中的順序。
spring interceptor也是這樣的執行順序,不過interceptor多一個配置參數order通過他也可以來實現interceptor的執行順序。很多應用場景中,執行順序還是重要的,比如cache和transaction interceptor的執行順序,很顯然cache應該在transaction之前,這樣發現命中了就不用打開事務,如果transaction在前,每次都打開事務即使cache命中,這是一個無謂東動作。
二、利用springMVC的interceptor實現頁面性能監控(Filter亦可)
調優第一步,找出耗時比較長的頁面進行優化。利用interceptor能輕易搞定。interceptor提供了preHandle和postHandle以及afterCompletion三個方法。preHandle調用controller具體方法之前調用,postHandle完成具體方法之后調用,afterCompletion完成對頁面的render以后調用,至此整個頁面渲染完成。也就是說我們在preHandle記錄開始的時間,在afterCompletion記錄結束的時間,就可或者整個頁面生成的時間。Spring自帶StopWatch工具類來實現時間跟蹤,關鍵一點interceptor不是線程安全的。我們需要借助threadlocal來實現線程安全。
- @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 攔截器實現分析
SpringMVC的攔截器不同于Spring的攔截器,SpringMVC具有統一的入口DispatcherServlet,所有的請求都通過DispatcherServlet,所以只需要在DispatcherServlet上做文章即可,DispatcherServlet也沒有代理,同時SpringMVC管理的Controller也不有代理。哪不難想到我們在執行controller之前做某些動作,執行完畢做某些動作,render完成做某些動作。SpringMVC的攔截器對應提供了三個preHandle,postHandle,afterCompletion方法。只需在三個方法內寫我們需要的邏輯就行,多了都是廢話,還是代碼實在。
- HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
- if (interceptors != null) {
- for (int i = 0; i < interceptors.length; i++) {
- HandlerInterceptor interceptor = interceptors[i];
- //ha.handle是調用具體的controller在此之前執行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());
完成調用之后,調用render(),最后執行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);
原文鏈接:http://exceptioneye.iteye.com/blog/1267248