攔截器(Interceptor)與過濾器(Filter)
點擊上方藍色字體,選擇“標星公眾號”
優(yōu)質文章,第一時間送達
作者 | 程序員巨輪
來源 | urlify.cn/fUfeUv
一、用戶的普通Http請求執(zhí)行順序

二、過濾器、攔截器添加后的執(zhí)行順序

三、攔截器(Interceptor)的基本定義
攔截器是面向切面(AOP)編程中應用的一種統(tǒng)一處理方案,就是在你的Controller、Servie或者一個Method調用一個Method,或者在Method調用一個Method之后,統(tǒng)一的進行處理的方案,基于Java的反射機制。
攔截器,在AOP(Aspect-Oriented Programming)中可以用于在某個方法或者字段被訪問之前,進行攔截,然后在之前或者之后加入某些統(tǒng)一的處理方法。攔截是AOP的一種具象的實現(xiàn)方式。
攔截器將很多service或者Controller中共有的行為提煉出來,在某些方法執(zhí)行的前后執(zhí)行,提煉為通用的處理方式,讓被攔截的方法都能享受這一共有的功能,讓代碼更加簡潔,同時,當共有的功能需要發(fā)生調整、變動的時候,不必修改很多的類或者方法,只要修改這個攔截器就可以了,可復用性很強。
Spring MVC 中的Interceptor攔截請求是通過HandlerInterceptor來實現(xiàn)的。
四、攔截器(Interceptor)必須實現(xiàn)的三個方法
1)總覽

2)preHandle(HttpServletRequest request, HttpServletResponse response, Object handle)方法
該方法將在請求處理之前進行調用。SpringMVC 中的Interceptor 是鏈式的調用的,在一個應用中或者說是在一個請求中可以同時存在多個Interceptor 。每個Interceptor 的調用會依據它的聲明順序依次執(zhí)行,而且最先執(zhí)行的都是Interceptor 中的preHandle 方法,所以可以在這個方法中進行一些前置初始化操作或者是對當前請求的一個預處理,也可以在這個方法中進行一些判斷來決定請求是否要繼續(xù)進行下去。該方法的返回值是布爾值Boolean類型的,當它返回為false 時,表示請求結束,后續(xù)的Interceptor 和Controller 都不會再執(zhí)行;當返回值為true 時就會繼續(xù)調用下一個Interceptor 的preHandle 方法,如果已經是最后一個Interceptor 的時候就會是調用當前請求的Controller 方法。
3) postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法 【前提:在當前所屬的Interceptor 的preHandle 方法的返回值為true 時才能被調用】
在當前請求進行處理之后,也就是Controller 方法調用之后執(zhí)行,但是它會在DispatcherServlet 進行視圖返回渲染之前被調用,所以我們可以在這個方法中對Controller 處理之后的ModelAndView 對象進行操作。postHandle 方法被調用的方向跟preHandle 是相反的,也就是說先聲明的Interceptor 的postHandle 方法反而會后執(zhí)行,這和Struts2 里面的Interceptor 的執(zhí)行過程有點類型。Struts2 里面的Interceptor 的執(zhí)行過程也是鏈式的,只是在Struts2 里面需要手動調用ActionInvocation 的invoke 方法來觸發(fā)對下一個Interceptor 或者是Action 的調用,然后每一個Interceptor 中在invoke 方法調用之前的內容都是按照聲明順序執(zhí)行的,而invoke 方法之后的內容就是反向的?! ?/p>
4)afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)方法 【前提:在當前所屬的Interceptor 的preHandle 方法的返回值為true 時才能被調用】
該方法將在整個請求結束之后,也就是在DispatcherServlet 渲染了對應的視圖之后執(zhí)行。這個方法的主要作用是用于進行資源清理工作的?! ?/p>
五、單個攔截器(Interceptor)的Demo實現(xiàn)
1)初始化攔截器
@Component
public class UserAccessInterceptor implements HandlerInterceptor {
private final Logger logger = LoggerFactory.getLogger(UserAccessInterceptor.class);
@Resource
private UserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
//1.獲取headers里的author-Cookie
//2.根據cookie使用userService查找當前user
//3.存在且激活 當前用戶信息設置到ThreadLocal return true;
//4.不存在 或 未激活 return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
//銷毀ThreadLocal的用戶信息
}
2)攔截器配置類
@Configuration
public class WebInterceptorConfig extends WebMvcConfigurationSupport {
@Autowired
private UserAccessInterceptor userAccessInterceptor;
//不攔截的URL集合
private static List<String> exclusionUrlList=new ArrayList<>();
static {
exclusionUrlList.add("/favicon.ico");
exclusionUrlList.add("/**/*.css");
exclusionUrlList.add("/**/*.js");
exclusionUrlList.add("/ok");
exclusionUrlList.add("/");
exclusionUrlList.add("/console/**");
exclusionUrlList.add("/index");
}
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.userAccessInterceptor)
.addPathPatterns("/**")
.excludePathPatterns(exclusionUrlList);
super.addInterceptors(registry);
}
}
六、攔截器(Interceptor)的兩種配置方式
1)同上 -> Demo實現(xiàn)
2)Spring MVC使用mvc:interceptors標簽
<mvc:interceptors>
<!-- 使用bean定義一個Interceptor,直接定義在mvc:interceptors根下面的Interceptor將攔截所有的請求 -->
<bean class="com.baidu.interceptor.UserAccessInterceptor"/>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/parent/**"/>
<bean class="com.baidu.interceptor.UserAccessInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
七、過濾器(Filter)的基本定義
Filter可以認為是Servlet的一種“加強版”,它主要用于對用戶請求進行預處理,也可以對HttpServletResponse進行后處理,是個典型的處理鏈。Filter也可以對用戶請求生成響應,這一點與Servlet相同,但實際上很少會使用Filter向用戶請求生成響應。使用Filter完整的流程是:Filter對用戶請求進行預處理,接著將請求交給Servlet進行處理并生成響應,最后Filter再對服務器響應進行后處理。
在Web中稱之為Filter,通過配置多個過濾器,Web系統(tǒng)可以對所有的Servlet請求進行一層一層的過濾,以完成一些特殊的功能。例如常用的資源訪問權限控制、特殊字符以及敏感詞過濾、響應信息壓縮等功能。
Servlet中的過濾器Filter是實現(xiàn)了javax.servlet.Filter接口的服務器端程序,主要的用途是設置字符集、控制權限、控制轉向、做一些業(yè)務邏輯判斷等。其工作原理是,只要你在web.xml文件配置好要攔截的客戶端請求,它都會幫你攔截到請求,此時你就可以對請求或響應(Request、Response)統(tǒng)一設置編碼,簡化操作;同時還可進行邏輯判斷,如用戶是否已經登陸、有沒有權限訪問該頁面等等工作。它是隨你的web應用啟動而啟動的,只初始化一次,以后就可以攔截相關請求,只有當你的web應用停止或重新部署的時候才銷毀。
八、過濾器(Filter)必須實現(xiàn)的三個方法
1)總覽

2)default void init(FilterConfig filterConfig) throws ServletException {}
用于完成Filter的初始化?! ?/p>
3)void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
實現(xiàn)過濾功能,該方法就是對每個請求及響應增加的額外處理。該方法可以實現(xiàn)對用戶請求進行預處理(ServletRequest request),也可實現(xiàn)對服務器響應進行后處理(ServletResponse response)—它們的分界線為是否調用了chain.doFilter(),執(zhí)行該方法之前,即對用戶請求進行預處理;執(zhí)行該方法之后,即對服務器響應進行后處理。
4) default void destroy() {}
用于Filter銷毀前,完成某些資源的回收。
九、單個過濾器(Filter)的Demo實現(xiàn)
1)TestFilter初始化
public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
System.out.println("我是Filter,執(zhí)行filterChain.doFilter(request,response)之前。");
filterChain.doFilter(request,response);
System.out.println("我是Filter,執(zhí)行filterChain.doFilter(request,response)之后。");
}
@Override
public void destroy() {
}
2)Filter配置類
@Slf4j
@Configuration
public class TestFilterConfiguration {//不攔截路徑
private static List<String> exclusionUrlList=new ArrayList<>();
//攔截路徑
private static List<String> inclusionUrlList=new ArrayList<>();
static {
exclusionUrlList.add("/favicon.ico");
exclusionUrlList.add("/**/*.css");
exclusionUrlList.add("/**/*.js");
exclusionUrlList.add("/ok");
inclusionUrlList.add("/api/**");
}
@Bean
public FilterRegistrationBean filterRegistration() {
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
registration.setFilter(new TestFilter());
registration.addInitParameter(FILTER_INIT_PARAM_EXCLUSION_URLS,String.join(",", exclusionUrlList));
registration.addInitParameter(AJAX_URL_PATTERNS,String.join(",", inclusionUrlList));
registration.addUrlPatterns("/*");
registration.setName("testFilter");
return registration;
}
}
十、過濾器(Filter)的三種配置方式
1)通過@WebFilter注解配置
1.初始化Filter
@WebFilter(urlPatterns = "/test001")
@Order(1) //order值越小,過濾器越靠前,此處配置無效
public class TestFilter implements Filter {
@Override
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
System.out.println("##############TestFilter init##############");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//在DispatcherServlet之前執(zhí)行
System.out.println("##############doFilter before##############");
filterChain.doFilter(servletRequest, servletResponse);
// 在視圖頁面返回給客戶端之前執(zhí)行,但是執(zhí)行順序在Interceptor之后
System.out.println("##############doFilter after##############");
}
@Override
public void destroy() {
System.out.println("##############TestFilter destroy##############");
}
}
//2.在啟動類添加 @ServletComponentScan
@SpringBootApplication
@ServletComponentScan
public class TestbootApplication {
public static void main(String[] args) {
SpringApplication.run(TestbootApplication.class, args);
}
}2)通過@Bean來配置
//1.初始化Filter
@Component
public class TestFilter3 implements Filter{
@Override
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
System.out.println("##############Filter3 init##############");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//在DispatcherServlet之前執(zhí)行
System.out.println("##############doFilter3 before##############");
filterChain.doFilter(servletRequest, servletResponse);
// 在視圖頁面返回給客戶端之前執(zhí)行,但是執(zhí)行順序在Interceptor之后
System.out.println("##############doFilter3 after##############");
}
@Override
public void destroy() {
System.out.println("##############Filter3 destroy##############");
}
}
//2.注冊到config
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean testFilter3RegistrationBean() {
FilterRegistrationBean registration = new FilterRegistrationBean(new TestFilter3());
registration.addUrlPatterns("/hello");
registration.setOrder(1); // 值越小越靠前,此處配置有效
return registration;
}
@Bean
public FilterRegistrationBean testFilter4RegistrationBean() {
FilterRegistrationBean registration = new FilterRegistrationBean(new TestFilter4());
registration.addUrlPatterns("/hello");
registration.setOrder(2);
return registration;
}
}
3)Spring MVC在web.xml進行配置
1.初始化Filter
2.web.xml文件中配置Filter
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
十一、攔截器和過濾器的區(qū)別

十二、攔截器和過濾器的作用/用途
過濾器用途:用于設置字符編碼、URL級別的權限控制,敏感詞匯的過濾
攔截器用途:攔截未登錄的用戶,攔截器和過濾器的功能相近
十三、總結
1.過濾器:所謂過濾器顧名思義是用來過濾的,在java web中,你傳入的request,response提前過濾掉一些信息,或者提前設置一些參數(shù),然后再傳入servlet或者struts的action進行業(yè)務邏輯,比如過濾掉非法url(不是login.do的地址請求,如果用戶沒有登陸都過濾掉),或者在傳入servlet或者struts的action前統(tǒng)一設置字符集,或者去除掉一些非法字符(聊天室經常用到的,一些罵人的話)。filter 流程是線性的, url傳來之后,檢查之后,可保持原來的流程繼續(xù)向下執(zhí)行,被下一個filter, servlet接收等.
2.java的攔截器 主要是用在插件上,擴展件上比如 hibernate spring struts2等 有點類似面向切片的技術,在用之前先要在配置文件即xml文件里聲明一段的那個東西。
粉絲福利:Java從入門到入土學習路線圖
??????

??長按上方微信二維碼 2 秒
感謝點贊支持下哈 
