Java interceptor 攔截器

百聯達發表於2016-12-08
一:攔截器的應用場景

1、日誌記錄:記錄請求資訊的日誌,以便進行資訊監控、資訊統計、計算PV(Page VIEW)等。
2、許可權檢查:如登入檢測,進入處理器檢測檢測是否登入,如果沒有直接返回到登入頁面;
3、效能監控:有時候系統在某段時間莫名其妙的慢,可以透過攔截器在進入處理器之前記錄開始時間,在處理完後記錄結束時間,從而得到該請求的處理時間(如果有反向代理,如apache可以自動記錄);
4、通用行為:讀取cookie得到使用者資訊並將使用者物件放入請求,從而方便後續流程使用,還有如提取Locale、Theme資訊等,只要是多個處理器都需要的即可使用攔截器實現。
5、OpenSessionInView:如Hibernate,在進入處理器開啟SESSION,在完成後關閉SESSION。

二:攔截器與過濾器的區別

 1、攔截器是基於Java的反射機制的,而過濾器是基於函式回撥。
 2、攔截器不依賴與servlet容器,過濾器依賴與servlet容器。
 3、攔截器只能對ACTION請求起作用,而過濾器則可以對幾乎所有的請求起作用。
 4、攔截器可以訪問ACTION上下文、值棧裡的物件,而過濾器不能訪問。
 5、在ACTION的生命週期中,攔截器可以多次被呼叫,而過濾器只能在容器初始化時被呼叫一次。
 6、攔截器可以獲取IOC容器中的各個bean,而過濾器就不行,這點很重要,在攔截器裡注入一個service,可以呼叫業務邏輯。


三:攔截器的實現

1.過濾器是JavaEE標準,採用函式回撥的方式進行。是在請求進入容器之後,還未進入Servlet之前進行預處理,並且在請求結束返回給前端這之間進行後期處理。

點選(此處)摺疊或開啟

  1. @Override
  2.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  3.         System.out.println("before...");
  4.         chain.doFilter(request, response);
  5.         System.out.println("after...");
  6.     }
chain.doFilter(request, response);這個方法的呼叫作為分水嶺。事實上呼叫Servlet的doService()方法是在chain.doFilter(request, response);這個方法中進行的。

2.攔截器是被包裹在過濾器之中的。

點選(此處)摺疊或開啟

  1. @Override
  2.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  3.         System.out.println("preHandle");
  4.         return true;
  5.     }

  6.     @Override
  7.     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  8.         System.out.println("postHandle");
  9.     }

  10.     @Override
  11.     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  12.         System.out.println("afterCompletion");
  13.     }
a.preHandle()這個方法是在過濾器的chain.doFilter(request, response)方法的前一步執行,也就是在 [System.out.println("before...")][chain.doFilter(request, response)]之間執行。
b.preHandle()方法之後,在RETURN ModelAndView之前進行,可以操控Controller的ModelAndView內容。
c.afterCompletion()方法是在過濾器返回給前端前一步執行,也就是在[chain.doFilter(request, response)][System.out.println("after...")]之間執行。

四:SpringMvc 攔截器和過濾器執行流程




五:例項

點選(此處)摺疊或開啟

  1. public class SystemAccessInterceper extends HandlerInterceptorAdapter {

  2.     private final String checkpointName = "DO_CHECKPOINT";
  3.     private final String shareType = "enjoylink_share";
  4.     String[] noFilters = ConfigureUtil.getStringArray("nofilter.urls");

  5.     private final Logger logger = Logger.getLogger(getClass());

  6.     @Override
  7.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
  8.             throws Exception {
  9.         String userAgent = request.getHeader("user-agent");
  10.         userAgent = StringUtils.startsWith(userAgent, Constants.USER_AGENT) ? userAgent
  11.                 : request.getHeader("x-user-agent");
  12.         String type = request.getParameter("shareType");
  13.         String requestUri = request.getRequestURI();
  14.         if (isFilter(requestUri)) {
  15.             return true;
  16.         }
  17.         logger.info("User-Agent:" + userAgent);
  18.         if (!StringUtils.equalsIgnoreCase(shareType, type) && (!StringUtils.startsWith(userAgent, Constants.USER_AGENT)
  19.                 || 2 > StringUtils.indexOfAny(userAgent, "|"))) {
  20.             logger.info("非法請求介面!!");
  21.             return false;
  22.         }

  23.         try {
  24.             if (StringUtils.contains(userAgent, "|")) {
  25.                 String[] agts = userAgent.split("\\|");
  26.                 request.setAttribute(Constants.HEAD_SYS, agts[1]);
  27.                 request.setAttribute(Constants.HEAD_VERSION, agts[2]);
  28.             }
  29.             request.setAttribute(Constants.LOG_ACCESS_TIME, System.currentTimeMillis());
  30.             return super.preHandle(request, response, handler);
  31.         }
  32.         catch (Exception e) {
  33.             logger.error("攔截器驗證失敗!", e);
  34.             return false;
  35.         }
  36.     }

  37.     /**
  38.      * TODO: 檢查是否需要過濾
  39.      *
  40.      * @param uri
  41.      * @return
  42.      */
  43.     private boolean isFilter(String uri) {
  44.         if (org.apache.commons.lang3.ArrayUtils.isEmpty(noFilters)) {
  45.             return Boolean.TRUE;
  46.         }

  47.         for (String noFilter : noFilters) {
  48.             if (StringUtils.contains(uri, noFilter)) {
  49.                 return Boolean.TRUE;
  50.             }
  51.         }

  52.         return Boolean.FALSE;
  53.     }

  54.     @Override
  55.     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
  56.             throws Exception {
  57.         super.afterCompletion(request, response, handler, ex);
  58.         Object obj = request.getAttribute(Constants.LOG_ACCESS_TIME);
  59.         if (null != obj) {
  60.             Object rb = request.getAttribute(Constants.RETURN_BODY);
  61.             long accessTime = (long) obj;
  62.             if (null == rb || StringUtils.isEmpty(rb.toString())) {
  63.                 logger.info("不進行檢查點處理!");
  64.                 logger.info("處理請求" + request.getRequestURI() +"耗時"+ (System.currentTimeMillis() - accessTime) + "毫秒!");
  65.                 return;
  66.             }
  67.             Object userId = request.getAttribute(Constants.REQUEST_USERID);
  68.             String system = request.getAttribute(Constants.HEAD_SYS).toString();
  69.             String version = request.getAttribute(Constants.HEAD_VERSION).toString();

  70.             logger.warn("處理請求|" + ((null == userId || "".equals(userId.toString())) ? "0" : userId.toString()) + "|"
  71.                     + system + "|" + version + "|" + request.getRequestURI() + "|"
  72.                     + GbdDateUtils.format(Calendar.getInstance().getTime(), "yyyy-MM-dd HH:mm:ss") + "|"
  73.                     + (System.currentTimeMillis() - accessTime) + "|毫秒!");
  74.             try {
  75.                 HandlerMethod handlerMethod = (HandlerMethod) handler;
  76.                 BusinessCheckPoint checkPoint = handlerMethod.getMethodAnnotation(BusinessCheckPoint.class);
  77.                 if (null != checkPoint) {
  78.                     String checkpointKey = checkPoint.checkName();

  79.                     // 業務處理結果為正確時。傳送積分或者信用累積
  80.                     if (null != rb && null != userId && StringUtils.isNotEmpty(userId.toString())) {
  81.                         ReturnBody returnBody = (ReturnBody) rb;
  82.                         // 檢查點檢測,如果是檢查點則傳送訊息
  83.                         if (returnBody.getCode() > 0) {
  84.                             // 傳送檢查點訊息
  85.                             CodeDesc codeDesc = new CodeDesc();
  86.                             codeDesc.setJmsType(checkpointName);
  87.                             codeDesc.setName(checkpointKey);
  88.                             codeDesc.setBusinessCode(returnBody.getCheckpointCode());
  89.                             codeDesc.setUserId(Integer.valueOf(userId.toString()));
  90.                             JmsUtil.pushToUserQueue(codeDesc);
  91.                         }
  92.                     }
  93.                 }
  94.             }
  95.             catch (Exception e) {
  96.                 logger.error("傳送積分訊息失敗!", e);
  97.             }
  98.         }
  99.     }

  100. }


點選(此處)摺疊或開啟

  1. <!-- 攔截器配置 -->
  2.     <mvc:interceptors>
  3.         <bean class="com.gemdale.ghome.business.intercepter.SystemAccessInterceper" />
  4.     </mvc:interceptors>



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28624388/viewspace-2130071/,如需轉載,請註明出處,否則將追究法律責任。

相關文章