Spring Boot中使用過濾器和攔截器

過濾器(Filter)和攔截器(Interceptor)是Web專案中常用的兩個功能,本文將簡單介紹在Spring Boot中使用過濾器和攔截器來計算Controller中方法的執行時長,並且簡單對比兩者的區別。

現有如下Controller:

@RestController @RequestMapping(“user”) public class UserController { @GetMapping(“/{id:\\d+}”) public void get(@PathVariable String id) { System。out。println(id); } }

下面透過配置過濾器和攔截器來實現對

get

方法執行時間計算的功能。

一、過濾器

定義一個

TestFilter

類,實現

javax。servlet。Filter

public class TestFilter implements Filter{ @Override public void init(FilterConfig filterConfig) throws ServletException { System。out。println(“過濾器初始化”); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System。out。println(“開始執行過濾器”); Long start = new Date()。getTime(); filterChain。doFilter(servletRequest, servletResponse); System。out。println(“【過濾器】耗時 ” + (new Date()。getTime() - start)); System。out。println(“結束執行過濾器”); } @Override public void destroy() { System。out。println(“過濾器銷燬”); } }

TestFilter

重寫了

Filter

的三個方法,方法名稱已經很直白的描述了其作用,這裡不再贅述。

要使該過濾器在Spring Boot中生效,還需要一些配置。這裡主要有兩種配置方式。

配置方式一

可透過在

TestFilter

上加上如下註解:

@Component @WebFilter(urlPatterns = {“/*”}) public class TestFilter implements Filter { 。。。 }

@Component

註解讓

TestFilter

成為Spring上下文中的一個Bean,

@WebFilter

註解的

urlPatterns

屬性配置了哪些請求可以進入該過濾器,

/*

表示所有請求。

啟動專案時可以看到控制檯輸出了

過濾器初始化

,啟動後訪問http://localhost:8080/user/1,控制檯輸出如下:

開始執行過濾器 1 【過濾器】耗時 31 結束執行過濾器

配置方式二

除了在過濾器類上加註解外,我們也可以透過

FilterRegistrationBean

來註冊過濾器。

定義一個

WebConfig

類,加上

@Configuration

註解表明其為配置類,然後透過

FilterRegistrationBean

來註冊過濾器:

@Configuration public class WebConfig { @Bean public FilterRegistrationBean testFilter() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); TestFilter testFilter = new TestFilter(); filterRegistrationBean。setFilter(testFilter); List urlList = new ArrayList<>(); urlList。add(“/*”); filterRegistrationBean。setUrlPatterns(urlList); return filterRegistrationBean; } }

FilterRegistrationBean

除了註冊過濾器

TestFilter

外還透過

setUrlPatterns

方法配置了URL匹配規則。重啟專案訪問http://localhost:8080/user/1,我們可以看到和上面一樣的效果。

透過過濾器我們只可以獲取到servletRequest物件,所以並不能獲取到方法的名稱,所屬類,引數等額外的資訊。

二、攔截器

定義一個

TestInterceptor

類,實現

org。springframework。web。servlet。HandlerInterceptor

介面:

public class TestInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { System。out。println(“處理攔截之前”); httpServletRequest。setAttribute(“startTime”, new Date()。getTime()); System。out。println(((HandlerMethod) o)。getBean()。getClass()。getName()); System。out。println(((HandlerMethod) o)。getMethod()。getName()); return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { System。out。println(“開始處理攔截”); Long start = (Long) httpServletRequest。getAttribute(“startTime”); System。out。println(“【攔截器】耗時 ” + (new Date()。getTime() - start)); } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { System。out。println(“處理攔截之後”); Long start = (Long) httpServletRequest。getAttribute(“startTime”); System。out。println(“【攔截器】耗時 ” + (new Date()。getTime() - start)); System。out。println(“異常資訊 ” + e); } }

TestInterceptor

實現了

HandlerInterceptor

介面的三個方法。

preHandle

方法在處理攔截之前執行,

postHandle

只有當被攔截的方法沒有丟擲異常成功時才會處理,

afterCompletion

方法無論被攔截的方法丟擲異常與否都會執行。

透過這三個方法的引數可以看到,相較於過濾器,攔截器多了Object和Exception物件,所以可以獲取的資訊比過濾器要多的多。但過濾器仍無法獲取到方法的引數等資訊,我們可以透過切面程式設計來實現這個目的。

要使攔截器在Spring Boot中生效,還需要如下兩步配置:

1。在攔截器類上加入

@Component

註解;

2。在

WebConfig

中透過

InterceptorRegistry

註冊過濾器:

@Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Autowired private TestInterceptor testInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry。addInterceptor(testInterceptor) 。addPathPatterns(“/**”) 。excludePathPatterns(“/**”, “/”, “/login”, “/register”); } }

啟動專案,訪問http://localhost:8080/user/1,控制檯輸出如下:

處理攔截之前 cn。ycf。controller。UserController get 1 開始處理攔截 【攔截器】耗時 24 處理攔截之後 【攔截器】耗時 24 異常資訊 null

從輸出中我們可以瞭解到三個方法的執行順序,並且三個方法都被執行了。

我們在

UserController

get

方法中手動丟擲一個異常:

@GetMapping(“/{id:\\d+}”) public void get(@PathVariable String id) { System。out。println(id); throw new RuntimeException(“user not exist”); }

重啟專案後,訪問http://localhost:8080/user/1,控制檯輸出如下:

處理攔截之前 cn。ycf。controller。UserController get 1 處理攔截之後 【攔截器】耗時 0 異常資訊 java。lang。RuntimeException: user not exist

可看到,

postHandle

方法並沒有被執行。

三、執行時機對比

我們將過濾器和攔截器都配置上,然後啟動專案訪問http://localhost:8080/user/1:

開始執行過濾器 處理攔截之前 cn。ycf。controller。UserController get 1 開始處理攔截 【攔截器】耗時 25 處理攔截之後 【攔截器】耗時 25 異常資訊 null 【過濾器】耗時 34 結束執行過濾器

可看到過濾器要先於攔截器執行,晚於攔截器結束。下圖很好的描述了它們的執行時間區別:

Spring Boot中使用過濾器和攔截器