開篇
在Web應用中,常常存在攔截全部或部分請求進行統(tǒng)一處理的應用場景,如權(quán)限校驗、參數(shù)校驗、性能監(jiān)控等。
在SpringMVC框架中,我們可以通過過濾器或攔截器實現(xiàn)相關(guān)功能,spring-boot-starter-web模塊底層實際就是SpringMVC框架,那么在SpringBoot項目中如何使用過濾器或攔截器呢?
過濾器與攔截器的區(qū)別
| 項目 | 過濾器Filter | 攔截器Interceptor | 說明 |
|---|---|---|---|
| 規(guī)范定義 | Servlet規(guī)范中定義,與SpringMVC框架無關(guān)。 | SpringMVC提供組件之一。 | 過濾器不依賴于Spring MVC框架。 |
| 調(diào)用順序 | 在Spring DispatchServlet執(zhí)行前 | 在Spring DispatchServlet中調(diào)用 | 故過濾器執(zhí)行會在攔截器之前。 |
| 容器資源 | 無法使用Spring容器資源,如果需要使用,可以通過ApplicationContext上下文對象獲取 | 攔截器本身就是Spring的容器資源,可以通過Ioc進行依賴注入直接使用容器資源。 | 單從Restful的接口請求來說,過濾器能做的攔截器都能做,從Bean管理角度,都建議使用攔截器實現(xiàn)。 |
過濾器與攔截器的使用
創(chuàng)建項目
創(chuàng)建一個maven項目spring-boot-examples-intercept,添加一下依賴:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
添加業(yè)務接口
添加一個HelloController類,實現(xiàn)一個Restful的測試接口/hello,代碼如下:
@RestController
@Slf4j
public class HelloController {
/**
* 測試請求方法
*
* @return
*/
@GetMapping("/hello")
public String hello() {
log.info("[{}]執(zhí)行{}方法!", this.getClass().getSimpleName(), "hello");
return "Hello!";
}
}
添加過濾器
過濾器開發(fā)
- 添加第一個過濾器
FirstFilter類,實現(xiàn)Filter接口,代碼如下:
@Slf4j
public class FirstFilter implements Filter {
/**
*
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("[{}]執(zhí)行{}方法:Before!", this.getClass().getSimpleName(), "doFilter");
//執(zhí)行下一個filter
filterChain.doFilter(servletRequest, servletResponse);
log.info("[{}]執(zhí)行{}方法:After!", this.getClass().getSimpleName(), "doFilter");
}
}
- 添加第二個過濾器
SecondFilter類,實現(xiàn)Filter接口,代碼如下:
@Slf4j
public class SecondFilter implements Filter {
/**
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("[{}]執(zhí)行{}方法:Before!", this.getClass().getSimpleName(), "doFilter");
//執(zhí)行下一個filter
filterChain.doFilter(servletRequest, servletResponse);
log.info("[{}]執(zhí)行{}方法:After!", this.getClass().getSimpleName(), "doFilter");
}
}
過濾器配置
配置過濾器有兩種方式,分別是通過配置類或者注解的方式,兩種方式都可以將過濾器配置到服務中,但是通過注解的方式我還沒發(fā)現(xiàn)指定過濾器順序的方法(通過@Order注解是無效的),所以如果需要指定過濾器執(zhí)行順序的,建議使用方式一,否則使用方式二代碼更簡潔。
-
方式一:通過配置類配置過濾器。
添加一個Spring Boot的配置類FilterConfig,里面注冊兩個FilterRegistrationBean類型的Bean,代碼如下:
@Configuration
public class FilterConfig {
/**
* 注冊第一個過濾器
* @return
*/
@Bean
public FilterRegistrationBean firstFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(new FirstFilter());
//可不設(shè)置,默認過濾路徑即為:/*
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(1);
return registrationBean;
}
/**
* 注冊第二個過濾器
* @return
*/
@Bean
public FilterRegistrationBean secondFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(new SecondFilter());
//可不設(shè)置,默認過濾路徑即為:/*
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(2);
return registrationBean;
}
}
-
方式二:通過注解配置過濾器
在FirstFilter和SecondFilter類上分別添加注解配置@WebFilter(urlPatterns = "/*"),由于@WebFilter并不是Spring提供的注解,所以還需要在項目的啟動類中添加注解配置@ServletComponentScan,告訴Spring掃描路徑,如下:
@SpringBootApplication
@ServletComponentScan(basePackages = "org.cent.springboot.example.intercept.filter")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
添加攔截器
開發(fā)攔截器
- 添加第一個攔截器
FirstInterceptor類,實現(xiàn)HandlerInterceptor接口,代碼如下:
@Slf4j
public class FirstInterceptor implements HandlerInterceptor {
/**
* controller方法調(diào)用前調(diào)用。
*
* @param request
* @param response
* @param handler
* @return 往下執(zhí)行則返回true,否則返回false
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("[{}]執(zhí)行{}方法!", this.getClass().getSimpleName());
return true;
}
/**
* controller方法調(diào)用后視圖渲染前執(zhí)行。
*
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("[{}]執(zhí)行{}方法!", this.getClass().getSimpleName(), "postHandle");
}
/**
* controller方法調(diào)用且視圖渲染完成后執(zhí)行
*
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("[{}]執(zhí)行{}方法!", this.getClass().getSimpleName(), "afterCompletion");
}
- 添加第二個攔截器
SecondInterceptor類,實現(xiàn)HandlerInterceptor接口,代碼如下:
@Slf4j
public class SecondInterceptor implements HandlerInterceptor {
/**
* controller方法調(diào)用前調(diào)用。
*
* @param request
* @param response
* @param handler
* @return 往下執(zhí)行則返回true,否則返回false
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("[{}]執(zhí)行{}方法!", this.getClass().getSimpleName(), "preHandle");
return true;
}
/**
* controller方法調(diào)用后視圖渲染前執(zhí)行。
*
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("[{}]執(zhí)行{}方法!", this.getClass().getSimpleName(), "postHandle");
}
/**
* controller方法調(diào)用且視圖渲染完成后執(zhí)行
*
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("[{}]執(zhí)行{}方法!", this.getClass().getSimpleName(), "afterCompletion");
}
配置攔截器
添加一個Spring Boot配置類,實現(xiàn)WebMvcConfigurer接口以覆蓋容器默認配置,代碼如下:
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
/**
* 重寫添加攔截器方法
*
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new FirstInterceptor())
.addPathPatterns("/**")
.order(1);//指定執(zhí)行順序,數(shù)值越小越優(yōu)先
registry.addInterceptor(new SecondInterceptor())
.addPathPatterns("/hello")
.order(2);//指定執(zhí)行順序,數(shù)值越小越優(yōu)先
}
}
啟動測試
啟動服務,訪問http://localhost:8120/hello接口,后臺輸入日志如下圖,會發(fā)現(xiàn)Filter執(zhí)行會在Interceptor之前,也驗證上面表格中的說法。

示例代碼
碼云:https://gitee.com/centy/spring-boot-examples/tree/master/spring-boot-examples-intercept
尾巴
過濾器依賴于Servlet容器,而Interceptor則為SpringMVC的一部分。過濾器能夠攔截所有請求,而Interceptor只能攔截Controller的請求,所以從覆蓋范圍來看,F(xiàn)ilter應用更廣一些。但是在Spring逐漸一統(tǒng)Java框架、前后端分離越演越烈,實際上大部分的應用場景,攔截器都可以滿足了。