Spring Boot 攔截器
定義攔截器
定義攔截器,只需要實現 HandlerInterceptor
接口。該接口中有三個方法: preHandle(……)
、postHandle(……)
和 afterCompletion(……)
。
preHandle(……)
方法:該方法的執行時機是,當某個 url 已經匹配到對應的 Controller 中的某個方法,且在這個方法執行之前。所以preHandle(……)
方法可以決定是否將請求放行,這是通過返回值來決定的,返回 true 則放行,返回 false 則不會向後執行。
postHandle(……)
方法:該方法的執行時機是,當某個 url 已經匹配到對應的 Controller 中的某個方法,且在執行完了該方法,但是在 DispatcherServlet 視圖渲染之前。所以在這個方法中有個 ModelAndView 參數,可以在此做一些修改動作。
afterCompletion(……)
方法:顧名思義,該方法是在整個請求處理完成後(包括視圖渲染)執行,這時做一些資源的清理工作,這個方法只有在preHandle(……)
被成功執行後並且返回 true 纔會被執行。
- 自定義攔截器。
public class MyInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(MyInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
String methodName = method.getName();
logger.info("====攔截到方法:{}====", methodName);
// 返回true纔會繼續執行,返回false則取消當前請求
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("執行完方法之後進執行(Controller方法調用之後),但是此時還沒進行視圖渲染");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("整個請求都處理完咯,DispatcherServlet也渲染了對應的視圖咯,此時我可以做一些清理的工作了");
}
}
配置攔截器
在 Spring Boot 2.0 之前,我們是直接繼承 WebMvcConfigurerAdapter 類,然後重寫 addInterceptors
方法來實現攔截器的配置。但是在 Spring Boot 2.0 之後,該方法已經被廢棄了(當然,也可以繼續用),取而代之的是 WebMvcConfigurationSupport 方法,如下:
@Configuration
public class MyInterceptorConfig extends WebMvcConfigurationSupport {
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
super.addInterceptors(registry);
}
}
在該配置中重寫 addInterceptors
方法,將我們上面自定義的攔截器添加進去,addPathPatterns
方法是添加要攔截的請求,這裏我們攔截所有的請求。這樣就配置好攔截器了,接下來寫一個 Controller 測試一下:
@Controller
@RequestMapping("/interceptor")
public class InterceptorController {
@RequestMapping("/test")
public String test() {
return "hello";
}
}
解決靜態資源被攔截問題
上文中已經介紹了攔截器的定義和配置,但是這樣是否就沒問題了呢?其實不然,如果使用上面這種配置的話,我們會發現一個缺陷,那就是靜態資源被攔截了。可以在 resources/static/ 目錄下放置一個圖片資源或者 html 文件,然後啓動項目直接訪問,即可看到無法訪問的現象。
也就是說,雖然 Spring Boot 2.0 廢棄了WebMvcConfigurerAdapter,但是 WebMvcConfigurationSupport 又會導致默認的靜態資源被攔截,這就需要我們手動將靜態資源放開。
如何放開呢?除了在 MyInterceptorConfig 配置類中重寫 addInterceptors
方法外,還需要再重寫一個方法:addResourceHandlers
,將靜態資源放開:
/**
* 用來指定靜態資源不被攔截,否則繼承WebMvcConfigurationSupport這種方式會導致靜態資源無法直接訪問
* @param registry
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
super.addResourceHandlers(registry);
}
這樣配置好之後,重啓項目,靜態資源也可以正常訪問了。如果你是個善於學習或者研究的人,那肯定不會止步於此,沒錯,上面這種方式的確能解決靜態資源無法訪問的問題,但是,還有更方便的方式來配置。
我們不繼承 WebMvcConfigurationSupport 類,直接實現 WebMvcConfigurer 接口,然後重寫 addInterceptors
方法,將自定義的攔截器添加進去即可,如下:
@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 實現WebMvcConfigurer不會導致靜態資源被攔截
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
}
}
這樣就非常方便了,實現 WebMvcConfigure 接口的話,不會攔截 Spring Boot 默認的靜態資源。
這兩種方式都可以,具體他們之間的細節,感興趣的讀者可以做進一步的研究,由於這兩種方式的不同,繼承 WebMvcConfigurationSupport 類的方式可以用在前後端分離的項目中,後臺不需要訪問靜態資源(就不需要放開靜態資源了);實現 WebMvcConfigure 接口的方式可以用在非前後端分離的項目中,因爲需要讀取一些圖片、css、js文件等等。