在Web應用中,經常存在攔截所有或部分請求進行統一處理的應用場景,如權限校驗、參數校驗、性能監控等。 在SpringMVC框架中,咱們能夠經過過濾器或攔截器實現相關功能,spring-boot-starter-web模塊底層實際就是SpringMVC框架,那麼在SpringBoot項目中如何使用過濾器或攔截器呢?git
項目 | 過濾器Filter | 攔截器Interceptor | 說明 |
---|---|---|---|
規範定義 | Servlet規範中定義,與SpringMVC框架無關。 | SpringMVC提供組件之一。 | 過濾器不依賴於Spring MVC框架。 |
調用順序 | 在Spring DispatchServlet執行前 | 在Spring DispatchServlet中調用 | 故過濾器執行會在攔截器以前。 |
容器資源 | 沒法使用Spring容器資源,若是須要使用,能夠經過ApplicationContext上下文對象獲取 | 攔截器自己就是Spring的容器資源,能夠經過Ioc進行依賴注 | 入直接使用容器資源。 |
建立一個maven項目spring-boot-examples-intercept,添加一下依賴:web
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
添加一個HelloController
類,實現一個Restful的測試接口/hello
,代碼以下:spring
@RestController @Slf4j public class HelloController { /** * 測試請求方法 * * @return */ @GetMapping("/hello") public String hello() { log.info("[{}]執行{}方法!", this.getClass().getSimpleName(), "hello"); return "Hello!"; } }
FirstFilter
類,實現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("[{}]執行{}方法:Before!", this.getClass().getSimpleName(), "doFilter"); //執行下一個filter filterChain.doFilter(servletRequest, servletResponse); log.info("[{}]執行{}方法:After!", this.getClass().getSimpleName(), "doFilter"); } }
SecondFilter
類,實現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("[{}]執行{}方法:Before!", this.getClass().getSimpleName(), "doFilter"); //執行下一個filter filterChain.doFilter(servletRequest, servletResponse); log.info("[{}]執行{}方法:After!", this.getClass().getSimpleName(), "doFilter"); } }
配置過濾器有兩種方式,分別是經過配置類或者註解的方式,兩種方式均可以將過濾器配置到服務中,可是經過註解的方式我還沒發現指定過濾器順序的方法(經過@Order
註解是無效的),因此若是須要指定過濾器執行順序的,建議使用方式一,不然使用方式二代碼更簡潔。後端
FilterConfig
,裏面註冊兩個FilterRegistrationBean類型的Bean,代碼以下:@Configuration public class FilterConfig { /** * 註冊第一個過濾器 * @return */ @Bean public FilterRegistrationBean firstFilter() { FilterRegistrationBean registrationBean = new FilterRegistrationBean(new FirstFilter()); //可不設置,默認過濾路徑即爲:/* registrationBean.addUrlPatterns("/*"); registrationBean.setOrder(1); return registrationBean; } /** * 註冊第二個過濾器 * @return */ @Bean public FilterRegistrationBean secondFilter() { FilterRegistrationBean registrationBean = new FilterRegistrationBean(new SecondFilter()); //可不設置,默認過濾路徑即爲:/* registrationBean.addUrlPatterns("/*"); registrationBean.setOrder(2); return registrationBean; } }
@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); } }
FirstInterceptor
類,實現HandlerInterceptor接口,代碼以下:@Slf4j public class FirstInterceptor implements HandlerInterceptor { /** * controller方法調用前調用。 * * @param request * @param response * @param handler * @return 往下執行則返回true,不然返回false * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("[{}]執行{}方法!", this.getClass().getSimpleName()); return true; } /** * controller方法調用後視圖渲染前執行。 * * @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("[{}]執行{}方法!", this.getClass().getSimpleName(), "postHandle"); } /** * controller方法調用且視圖渲染完成後執行 * * @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("[{}]執行{}方法!", this.getClass().getSimpleName(), "afterCompletion"); }
SecondInterceptor
類,實現HandlerInterceptor接口,代碼以下:@Slf4j public class SecondInterceptor implements HandlerInterceptor { /** * controller方法調用前調用。 * * @param request * @param response * @param handler * @return 往下執行則返回true,不然返回false * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("[{}]執行{}方法!", this.getClass().getSimpleName(), "preHandle"); return true; } /** * controller方法調用後視圖渲染前執行。 * * @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("[{}]執行{}方法!", this.getClass().getSimpleName(), "postHandle"); } /** * controller方法調用且視圖渲染完成後執行 * * @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("[{}]執行{}方法!", this.getClass().getSimpleName(), "afterCompletion"); }
添加一個Spring Boot配置類,實現WebMvcConfigurer接口以覆蓋容器默認配置,代碼以下:springboot
@Configuration public class InterceptorConfig implements WebMvcConfigurer { /** * 重寫添加攔截器方法 * * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new FirstInterceptor()) .addPathPatterns("/**") .order(1);//指定執行順序,數值越小越優先 registry.addInterceptor(new SecondInterceptor()) .addPathPatterns("/hello") .order(2);//指定執行順序,數值越小越優先 } }
啓動服務,訪問http://localhost:8120/hello接口,後臺輸入日誌以下圖,會發現Filter執行會在Interceptor以前,也驗證上面表格中的說法。 app
碼雲:https://gitee.com/centy/spring-boot-examples/tree/master/spring-boot-examples-intercept框架
過濾器依賴於Servlet容器,而Interceptor則爲SpringMVC的一部分。過濾器可以攔截全部請求,而Interceptor只能攔截Controller的請求,因此從覆蓋範圍來看,Filter應用更廣一些。可是在Spring逐漸一統Java框架、先後端分離越演越烈,實際上大部分的應用場景,攔截器均可以知足了。前後端分離