Spring MVC攔截器的使用
由David發表在天碼營
Spring WebMvc框架中的Interceptor,與Servlet API中的Filter十分類似,用於對Web請求進行預處理/後處理。通常情況下這些預處理/後處理邏輯是通用的,可以被應用於所有或多個Web請求,例如:
- 記錄Web請求相關日誌,可以用於做一些信息監控、統計、分析
- 檢查Web請求訪問許可權,例如發現用戶沒有登錄後,重定向到登錄頁面
- 打開/關閉資料庫連接——預處理時打開,後處理關閉,可以避免在所有業務方法中都編寫類似代碼,也不會忘記關閉資料庫連接
Spring MVC請求處理流程
上圖是Spring MVC框架處理Web請求的基本流程,請求會經過DispatcherServlet的分發後,會按順序經過一系列的Interceptor並執行其中的預處理方法,在請求返回時同樣會執行其中的後處理方法。
HandlerInterceptor介面
Spring MVC中攔截器是實現了HandlerInterceptor介面的Bean:
public interface HandlerInterceptor {n boolean preHandle(HttpServletRequest request, n HttpServletResponse response, n Object handler) throws Exception;nn void postHandle(HttpServletRequest request, n HttpServletResponse response, n Object handler, ModelAndView modelAndView) throws Exception;nn void afterCompletion(HttpServletRequest request, n HttpServletResponse response, n Object handler, Exception ex) throws Exception;n}n
- preHandle():預處理回調方法,若方法返回值為true,請求繼續(調用下一個攔截器或處理器方法);若方法返回值為false,請求處理流程中斷,不會繼續調用其他的攔截器或處理器方法,此時需要通過response產生響應;
- postHandle():後處理回調方法,實現處理器的後處理(但在渲染視圖之前),此時可以通過modelAndView對模型數據進行處理或對視圖進行處理
- afterCompletion():整個請求處理完畢回調方法,即在視圖渲染完畢時調用
HandlerInterceptor有三個方法需要實現,但大部分時候可能只需要實現其中的一個方法,HandlerInterceptorAdapter是一個實現了HandlerInterceptor的抽象類,它的三個實現方法都為空實現(或者返回true),繼承該抽象類後可以僅僅實現其中的一個方法:
public class Interceptor extends HandlerInterceptorAdapter {nn public boolean preHandle(HttpServletRequest request,n HttpServletResponse response,n Object handler) throws Exception {n System.out.println("This is interceptor.");n return true;n }n}n
配置Interceptor
定義HandlerInterceptor後,需要在MVC配置中將它們應用於特定的URL中,下面是一個配置的例子:
@Configurationn@EnableWebMvcnpublic class WebConfig extends WebMvcConfigurerAdapter {nn @Overriden public void addInterceptors(InterceptorRegistry registry) {n registry.addInterceptor(new LocaleInterceptor());n registry.addInterceptor(new ThemeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");n registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");n }nn}n
對應的XML配置是:
<mvc:interceptors>n <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />n <mvc:interceptor>n <mvc:mapping path="/**"/>n <mvc:exclude-mapping path="/admin/**"/>n <bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor" />n </mvc:interceptor>n <mvc:interceptor>n <mvc:mapping path="/secure/*"/>n <bean class="org.example.SecurityInterceptor" />n </mvc:interceptor>n</mvc:interceptors>n
實例:用戶登錄檢查
根據前面幾個小節的學習,現在需要實現用戶登錄檢查——如果發現用戶沒有登錄,跳轉到登錄頁面,登錄成功後跳轉回之前訪問的頁面。
Interceptor實現檢查邏輯
public class LoginInterceptor extends HandlerInterceptorAdapter {nn @Overriden public boolean preHandle(HttpServletRequest request,n HttpServletResponse response,n Object handler) throws Exception {n if (request.getSession().getAttribute(Constants.USER_SESSION_ATTR) != null) {n return true;n }nn response.sendRedirect("/login?next=".concat(request.getRequestURI()));n return false;n }n}n
為了實現跳轉登錄頁面登錄成功後能夠返回當前頁面,在Interceptor中將當前URL作為/login的參數next
LoginController
@Controllern@RequestMapping("/login")npublic class LoginController {nn public static final Logger logger = LoggerFactory.getLogger(LoginController.class);nn @RequestMapping(method = RequestMethod.GET)n public String loginPage(@RequestParam("next") Optional<String> next) {n logger.info("next = {}", next);n return "login";n }nn @RequestMapping(method = RequestMethod.POST)n public String login(@RequestParam("next") Optional<String> next, HttpSession session) {n logger.info("next = {}", next);n session.setAttribute(Constants.USER_SESSION_ATTR, "username");n return "redirect:".concat(next.orElse("/"));n }nn}n
在登錄的POST方法中,除了將Session中放入user對象外,跳轉到next以便回到登錄前的頁面。
配置Interceptor
需要注意的是,登錄頁面本身(包括POST請求)不能應用Interceptor來攔截,否則會陷入無限循環中:
@Configurationnpublic class WebMvcConfig extends WebMvcConfigurerAdapter {nn @Overriden public void addInterceptors(InterceptorRegistry registry) {n registry.addInterceptor(new LoginInterceptor())n .addPathPatterns("/**")n .excludePathPatterns("/login");n }n}n
歡迎關注天碼營微信公眾號: TMY-EDU
小編重點推薦:
Spring MVC實戰入門訓練
Spring Data JPA實戰入門訓練
Java Web實戰訓練
Node.js全棧開發
更多精彩內容請訪問天碼營網站推薦閱讀:
※Spring boot與Spring cloud 是什麼關係?
※關於Spring MVC的教程和例子?
※springboot怎麼學?
※spring-jdbc 目前還是一個主流的廣泛使用的持久化框架嗎?
TAG:SpringMVC框架 | Spring | SpringBoot |