Interceptor 介紹
攔截器 (Interceptor) 同 Filter 過濾器一樣,它們都是面向切面編程——AOP 的具體實現。
可以使用 Interceptor 來執行某些任務,例如在 Controller 處理請求之前編寫日誌,添加或更新配置等。
在 Spring 中,當請求發送到 Controller 時,在被 Controller 處理之前,必須經過 Interceptors。
過濾器和攔截器的區別
- 過濾器(Filter):當有一堆東西的時候,只希望選擇符合你要求的某一些東西。定義這些要求的工具,就是過濾器。
- 攔截器(Interceptor):在一個流程正在進行的時候,希望干預它的進展,甚至終止它進行,這是攔截器做的事情。
自定義 Interceptor
自定義 Interceptor 需要實現 org.springframework.web.servlet.HandlerInterceptor
接口或繼承 org.springframework.web.servlet.handler.HandlerInterceptorAdapter
類,並且需要重寫下面3個方法:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
注意: preHandle 方法返回 **true **或 false。如果返回 true,則意味着請求將繼續到達 Controller 被處理。
每個請求可能會通過多個攔截器。
示例
LogInterceptor
用於過濾所有請求:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class LogInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
long startTime = System.currentTimeMillis();
System.out.println("\n-------- LogInterception.preHandle --- ");
System.out.println("Request URL: " + request.getRequestURL());
System.out.println("Start Time: " + System.currentTimeMillis());
request.setAttribute("startTime", startTime);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("\n-------- LogInterception.postHandle --- ");
System.out.println("Request URL: " + request.getRequestURL());
// You can add attributes in the modelAndView
// and use that in the view page
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("\n-------- LogInterception.afterCompletion --- ");
long startTime = (Long) request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
System.out.println("Request URL: " + request.getRequestURL());
System.out.println("End Time: " + endTime);
System.out.println("Time Taken: " + (endTime - startTime));
}
}
OldLoginInterceptor
是一個攔截器,如果用戶輸入已經被廢棄的鏈接“/admin/oldLogin”,它將重定向到新的 “/admin/login”:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class OldLoginInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("\n-------- OldLoginInterceptor.preHandle --- ");
System.out.println("Request URL: " + request.getRequestURL());
System.out.println("Sorry! This URL is no longer used, Redirect to /admin/login");
response.sendRedirect(request.getContextPath() + "/admin/login");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// This code will never be run.
System.out.println("\n-------- OldLoginInterceptor.postHandle --- ");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// This code will never be run.
System.out.println("\n-------- QueryStringInterceptor.afterCompletion --- ");
}
}
AdminInterceptor
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class AdminInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("\n-------- AdminInterceptor.preHandle --- ");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("\n-------- AdminInterceptor.postHandle --- ");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("\n-------- AdminInterceptor.afterCompletion --- ");
}
}
配置攔截器
import github.javaguide.springbootfilter.interceptor.AdminInterceptor;
import github.javaguide.springbootfilter.interceptor.LogInterceptor;
import github.javaguide.springbootfilter.interceptor.OldLoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// LogInterceptor apply to all URLs.
registry.addInterceptor(new LogInterceptor());
// Old Login url, no longer use.
// Use OldURLInterceptor to redirect to a new URL.
registry.addInterceptor(new OldLoginInterceptor())
.addPathPatterns("/admin/oldLogin");
// This interceptor apply to URL like /admin/*
// Exclude /admin/oldLogin
registry.addInterceptor(new AdminInterceptor())
.addPathPatterns("/admin/*")
.excludePathPatterns("/admin/oldLogin");
}
}
自定義 Controller 驗證攔截器
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class InterceptorTestController {
@RequestMapping(value = { "/", "/test" })
public String test(Model model) {
System.out.println("\n-------- MainController.test --- ");
System.out.println(" ** You are in Controller ** ");
return "test";
}
// This path is no longer used.
// It will be redirected by OldLoginInterceptor
@Deprecated
@RequestMapping(value = { "/admin/oldLogin" })
public String oldLogin(Model model) {
// Code here never run.
return "oldLogin";
}
@RequestMapping(value = { "/admin/login" })
public String login(Model model) {
System.out.println("\n-------- MainController.login --- ");
System.out.println(" ** You are in Controller ** ");
return "login";
}
}
thymeleaf 模板引擎
test.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Spring Boot Mvc Interceptor example</title>
</head>
<body>
<div style="border: 1px solid #ccc;padding: 5px;margin-bottom:10px;">
<a th:href="@{/}">Home</a>
|
<a th:href="@{/admin/oldLogin}">/admin/oldLogin (OLD URL)</a>
</div>
<h3>Spring Boot Mvc Interceptor</h3>
<span style="color:blue;">Testing LogInterceptor</span>
<br/><br/>
See Log in Console..
</body>
</html>
login.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Spring Boot Mvc Interceptor example</title>
</head>
<body>
<div style="border: 1px solid #ccc;padding: 5px;margin-bottom:10px;">
<a th:href="@{/}">Home</a>
|
<a th:href="@{/admin/oldLogin}">/admin/oldLogin (OLD URL)</a>
</div>
<h3>This is Login Page</h3>
<span style="color:blue">Testing OldLoginInterceptor & AdminInterceptor</span>
<br/><br/>
See more info in the Console.
</body>
</html>
測試結果
測試用戶訪問 http://localhost:8080/ 的時候, LogInterceptor 記錄相關信息(頁面地址,訪問時間),並計算 Web服務器處理請求的時間。另外,頁面會被渲染成 test.html
。
當用戶訪問 http://localhost:8080/admin/oldLogin 也就是舊的登錄頁面(不再使用)時, OldLoginInterceptor 將請求重定向 http://localhost:8080/admin/login 頁面會被渲染成正常的登錄頁面 login.html
。