攔截器原理:
攔截器的原理很簡單,是 AOP 的一種實(shí)現(xiàn),專門攔截對(duì)動(dòng)態(tài)資源的后臺(tái)請(qǐng)求,即攔截對(duì)控制層的請(qǐng)求。使用場(chǎng)景比較多的是判斷用戶是否有權(quán)限請(qǐng)求后臺(tái),更拔高一層的使用場(chǎng)景也有,比如攔截器可以結(jié)合 websocket 一起使用,用來攔截 websocket 請(qǐng)求,然后做相應(yīng)的處理等等。
注冊(cè)攔截器
步驟01 創(chuàng)建一個(gè)spring boot項(xiàng)目,添加spring-boot-starter-web依賴
步驟02 創(chuàng)建攔截器實(shí)現(xiàn)HandlerInterceptor接口
package xintianweng;
import org.apache.catalina.filters.ExpiresFilter;
import org.apache.catalina.servlet4preview.http.HttpServletRequest;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletResponse;
/**
* 創(chuàng)建一個(gè)攔截器
* @author 信天翁
* 2021/3/29 10:00 下午
**/
public class MyInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler){
System.out.println("MyInterceptor>>>preHandle");
return true;
}
public void postHandle(HttpServletRequest request, ExpiresFilter.XHttpServletResponse response, Object handler, ModelAndView modelAndView){
System.out.println("MyInterceptor>>>postHandle");
}
public void afterCompletion(HttpServletRequest request, ExpiresFilter.XHttpServletResponse response, Object handler,Exception ex){
System.out.println("MyInterceptor>>>afterCompletion");
}
}
攔截器中的方法將按preHandle??Controller??postHandle??afterCompletion的順序執(zhí)行。
注意,只有當(dāng)preHandle方法返回true時(shí)后面的方法才會(huì)執(zhí)行。
當(dāng)攔截器鏈內(nèi)存在多個(gè)攔截器時(shí),postHandler在攔截器鏈內(nèi)的所有攔截器返回成功時(shí)才會(huì)調(diào)用,而afterCompletion只有preHandle返回true才調(diào)用,但若攔截器鏈內(nèi)的第一個(gè)攔截器的preHandle方法返回false,則后面的方法都不會(huì)執(zhí)行。
步驟03 配置攔截器。定義配置類進(jìn)行攔截器的配置
package xintianweng;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 配置攔截器
* @author 信天翁
* 2021/3/29 10:00 下午
**/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/hello");
}
}
preHandle(……) 方法:該方法的執(zhí)行時(shí)機(jī)是,當(dāng)某個(gè) url 已經(jīng)匹配到對(duì)應(yīng)的 Controller 中的某個(gè)方法,且在這個(gè)方法執(zhí)行之前。所以 preHandle(……) 方法可以決定是否將請(qǐng)求放行,這是通過返回值來決定的,返回 true 則放行,返回 false 則不會(huì)向后執(zhí)行。
postHandle(……) 方法:該方法的執(zhí)行時(shí)機(jī)是,當(dāng)某個(gè) url 已經(jīng)匹配到對(duì)應(yīng)的 Controller 中的某個(gè)方法,且在執(zhí)行完了該方法,但是在 DispatcherServlet 視圖渲染之前。所以在這個(gè)方法中有個(gè) ModelAndView 參數(shù),可以在此做一些修改動(dòng)作。
afterCompletion(……) 方法:顧名思義,該方法是在整個(gè)請(qǐng)求處理完成后(包括視圖渲染)執(zhí)行,這時(shí)做一些資源的清理工作,這個(gè)方法只有在 preHandle(……) 被成功執(zhí)行后并且返回 true 才會(huì)被執(zhí)行。
自定義類實(shí)現(xiàn)WebMvcConfigurer接口,實(shí)現(xiàn)接口中的addInterceptors方法。其中,
addPathPatterns是用來表示攔截路徑。
excludePathPatterns是用來表示排除的路徑。
templates下創(chuàng)建hello.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
spring boot !
</body>
</html>
執(zhí)行springboot啟動(dòng)類,控制臺(tái)輸出如下:
MyInterceptor>>>>preHandle
HELLO SPRING BOOT!
MyInterceptor>>>>postHandle
MyInterceptor>>>>afterCompletion
總結(jié):
①:首先會(huì)在攔截到Controller中的“/hello”時(shí)先執(zhí)行preHandle里的邏輯。輸出“MyInterceptor>>>>preHandle”
②:然后執(zhí)行“/hello”對(duì)應(yīng)映射的方法邏輯,輸出“HELLO SPRING BOOT!”
③:成功執(zhí)行“/hello”對(duì)應(yīng)映射的方法邏輯,然后在 DispatcherServlet 視圖渲染之前執(zhí)行postHandle方法里的邏輯。
④:整個(gè)請(qǐng)求結(jié)束后,然后在這里執(zhí)行一些其他輔助邏輯,包括資源清理等。
攔截器處理靜態(tài)資源被攔截的問題
spring boot 2.0以后廢棄了WebMvcConfigurerAdapter,但是WebMvcConfigurationSupport 又會(huì)導(dǎo)致默認(rèn)的靜態(tài)資源被攔截
手動(dòng)放開靜態(tài)資源攔截:(和上面的手動(dòng)配置攔截器一樣,在原來的>addInterceptors方法外,還需要重寫addResourceHandlers,將資源放開)
protected void addResourceHandlers(ResourceHandlerRegistry registry){
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
super.addResourceHandlers(registry);
}