在許多的Java Web 框架都有實現Interceptor 的方法,例如Struct2 是Interceptor就是Spring MVC 就是HandlerInterceptor,而Interceptor 介於DispatcherServlet 與Controller 之間,可以在請求到達Controller 前或回應回傳出Controller 時進行攔截,攔截成功一樣可以實作一些自定義的業務邏輯或對回應資料進行修改。
建立Interceptor 的常見方法一樣有兩種,一種是實作org.springframework.web.servlet.HandlerInterceptor 介面,另一種則是繼承org.springframework.web.servlet.handler.HandlerInterceptorAdapter 類別,不管是哪種方法都要覆寫三個方法。
preHandle(...)
: 該方法是在請求進入Controller 之前執行,用來進行一些初始化或預處理,也可以判斷是否繼續執行下去。
該方法返回值為Boolean 值,返回false 表示請求結束,後續Interceptor 和Controller 均不會執行,而返回true 表示請求繼續,會繼續調用下一個Interceptor 或是Controller。
postHandle(...)
: 該方法是Controller 之後執行的方法,會在DispatcherServlet 進行視圖渲染以前被調用,所以可以在這個方法中隊Controller 處理之後的ModelAndView 物件進行操作。
afterCompletion(...)
: 該方法是在整個請求結束之後,在DispatcherServlet 對視圖渲染完成後執行,主要進行資源清理。
package com.example.iThomeIronMan.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.example.iThomeIronMan.controller.LoginRegisterController;
import com.example.iThomeIronMan.model.Member;
@Component
public class IndexInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(LoginRegisterController.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// TODO Auto-generated method stub
String uri = new String(request.getRequestURI());
HttpSession session = request.getSession();
Member member = (Member) session.getAttribute("MemberSession");
// 已登入
if(member != null) {
logger.info(member.getName() + " 訪問 " + uri);
return true;
}
// 未登入
else {
return false;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
最後設定攔截器使用InterceptorRegistry 的三個方法。
addInterceptor()
: 註冊攔截器。addPathPatterns()
: 新增攔截路徑。excludePathPatterns()
: 新增不攔截路徑。package com.example.iThomeIronMan.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.example.iThomeIronMan.interceptor.IndexInterceptor;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Autowired
private IndexInterceptor indexInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// TODO Auto-generated method stub
registry.addInterceptor(indexInterceptor)
.addPathPatterns("/information/**", "/updatePassword/**")
.excludePathPatterns("/login/**", "/register/**", "/css/**", "/images/**", "/js/**");
}
}
我們可以根據上面的介紹畫出上面這個流程圖,Filter 的作用區間在請求到達Servlet 之前,Interceptor 在Dsipatcher Servlet 接收請求之後在交由Controller 之前;Filter 面對的是請求而Interceptor 面對的是Controller,兩者的主要應用場景與功能幾乎類似,例如登入認證、Session 檢查和處理等等,而主要的差異就是越早的過濾或攔截對系統性能的消耗越少,在應用上就要由開發人員根據實際業務情況綜合考慮了。
HandlerInterceptors vs. Filters in Spring MVC
Spring Boot之Filter和Interceptor的對比分析