與 Servlet 類似,過濾器是一些 web 應用程序組件,能夠綁定到一個 web 應用程序中。可是與其餘 web 應用組件不一樣的是,過濾器是「鏈」在容器的處理過程當中的。這就意味着它們能夠在請求達到 servlet 以前對其進行訪問,也能夠在響應信息返回到客戶端以前對其進行攔截。這種訪問使得過濾器能夠檢查並修改請求和響應的內容。java
init :web
Filter 的初始化,在 Servlet 容器建立過濾器實例的時候調用,以確保過濾器可以正常工做。在 init() 方法執行過程當中遇到以下問題時,web 容器將不會配置其可用 :spring
doFilter :cookie
Filter 的核心方法,用於對每一個攔截到的請求作一些設定好的操做。app
典型用途以下:框架
destory :ide
Filter 的銷燬,在 Servlet 容器銷燬過濾器實例時調用,以釋放其佔用的資源。只有在 doFilter() 方法中的全部線程退出或超時後,web 容器纔會調用此方法。函數
首先咱們須要建立一個類,讓它實現 Filter 接口,而後重寫接口中的方法:post
package com.demo.demofilter.demofilter.filter;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Component
@Order(1) // 過濾順序,值越小越先執行
@WebFilter(urlPatterns = "/demoFilter", filterName = "filterTest")
public class DemoFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("filter初始化中...");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("\ndoFilter()開始執行:發往 " + ((HttpServletRequest) servletRequest).getRequestURL().toString() + " 的請求已被攔截");
System.out.println("檢驗接口是否被調用,嘗試獲取contentType以下: " + servletResponse.getContentType());
// filter的鏈式調用;將請求轉給下一條過濾鏈
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("檢驗接口是否被調用,嘗試獲取contentType以下: " + servletResponse.getContentType());
System.out.println("doFilter()執行結束。\n");
}
@Override
public void destroy() {
System.out.println("filter銷燬中...");
}
}
複製代碼
而後建立一個 Controller,對外提供兩條請求路徑:性能
package com.demo.demofilter.demofilter.filter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("demoFilter")
public class FilterController {
@GetMapping("hello")
public String hello() {
System.out.println("接口被調用:hello() ");
return "hello filter";
}
@GetMapping("hi")
public String hi() {
System.out.println("接口被調用:hi()");
return "hi filter";
}
}
複製代碼
啓動項目,能夠看到咱們的過濾器已經隨着程序的啓動被成功初始化了
分別對這兩個接口發送請求:
最後使項目中止運行,則過濾器隨之銷燬
當咱們配置了多個 filter,且一個請求可以被屢次攔截時,該請求將沿着 客戶端 -> 過濾器1 -> 過濾器2 -> servlet -> 過濾器2 -> 過濾器1 -> 客戶端
鏈式流轉,以下圖所示 :
以上面的代碼爲例,因爲咱們只定義了一個過濾器,在執行到 filterChain.doFilter(servletRequest, servletResponse);
的時候,請求就會被直接轉送到 servlet 中進行調用。
因此咱們須要稍微給它改造一下,看看再添加一個 DemoFilter2 會發生什麼:
package com.demo.demofilter.demofilter.filter;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Component
@Order(2) // 過濾順序,值越小越先執行,值相同或不指定時按filterName排序
// 注意這裏的urlPatterns要與前面保持一致
@WebFilter(urlPatterns = "/demoFilter", filterName = "filterTest2")
public class DemoFilter2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("filter2初始化中...");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("\ndoFilter2()開始執行:發往 " + ((HttpServletRequest) servletRequest).getRequestURL().toString() + " 的請求已被攔截");
System.out.println("檢驗接口是否被調用,嘗試獲取contentType以下: " + servletResponse.getContentType());
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("檢驗接口是否被調用,嘗試獲取contentType以下: " + servletResponse.getContentType());
System.out.println("doFilter2()執行結束。\n");
}
@Override
public void destroy() {
System.out.println("filter2銷燬中...");
}
}
複製代碼
運行結果以下:
能夠看出,當請求同時知足多個過濾器的過濾條件時,filterChain.doFilter()
會將其按必定順序(能夠經過 @Order 指定)依次傳遞到下一個 filter,直到進入 servlet 進行接口的實際調用。調用完成後,響應結果將沿着原路返回,並在再一次通過各個 filter 後,最終抵達客戶端。
攔截器是 AOP 的一種實現策略,用於在某個方法或字段被訪問前對它進行攔截,而後在其以前或以後加上某些操做。同 filter 同樣,interceptor 也是鏈式調用。每一個 interceptor 的調用會依據它的聲明順序依次執行。通常來講攔截器能夠用於如下方面 :
preHandler:
方法的前置處理,將在請求處理以前被調用。通常用它來對方法進行一些前置初始化操做,或是對當前請求作一些預處理;此外也能夠用來進行權限校驗之類的判斷,來決定請求是否要繼續進行下去。
該方法返回一個布爾值,若該值爲 false,則請求結束,後續的 Interceptor 和 Controller 都不會再執行;若該值爲 true,則會繼續調用下一個 Interceptor 的 preHandler() 方法,若是已經到達最後一個 interceptor 了,就會調用當前請求的 Controller。
postHandler:
方法的後置處理,將在請求處理以後被調用。雖然是在 Controller 方法調用後再執行,但它的調用依然在 DispatcherServlet 進行視圖渲染並返回以前,因此通常能夠經過它對 Controller 處理以後的 ModelAndView 對象進行操做。
afterCompletion:
在整個請求處理完成(包括視圖渲染)後執行,主要用來進行一些資源的清理工做。
一樣,首先建立一個類,讓它實現 HandlerInterceptor 接口,而後重寫接口中的方法 :
package com.demo.demofilter.demofilter.interceptor;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class DemoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
System.out.println("afterHandle");
}
}
複製代碼
緊接着須要對攔截器進行註冊,指明使用哪一個攔截器,及該攔截器對應攔截的 URL :
package com.demo.demofilter.demofilter.interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 若是有多個攔截器,繼續registry.add往下添加就能夠啦
registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/demoInterceptor/**");
}
}
複製代碼
最後是 Controller
package com.demo.demofilter.demofilter.interceptor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("demoInterceptor")
public class InterceptorController {
@GetMapping("hello")
public String hello() {
System.out.println("接口被調用:hello() ");
return "hello interceptor";
}
@GetMapping("hi")
public String hi() {
System.out.println("接口被調用:hi()");
return "hi interceptor";
}
}
複製代碼
運行結果以下 :
在 Http 的請求執行過程當中,要通過如下幾個步驟 :
因此,只有通過 DispatcherServlet 的請求才會被攔截器捕獲,而咱們自定義的 Servlet 請求則不會被攔截的。
做爲AOP思想的兩種典型實現,過濾器與攔截器有着許多類似的地方。而二者最大的區別在於 :過濾器是在 Servlet 規範中定義的,是由 Servlet 容器支持的;攔截器是在 Spring 容器內的,由 Spring 框架支持。
所以,做爲 Spring 的一個組件,攔截器能夠經過IOC容器進行管理,獲取其中的各個 bean 實例,對 spring 中的各類資源、對象,如 Service 對象、數據源、事務管理等進行調用;而過濾器則不能。
總的來講,二者主要在以下方面存在着差別 :
最後補兩張圖嘿嘿 :
filter、servlet、interceptor 觸發時機
doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
的入參是 ServletRequest,而不是 HttpServletRequest,由於過濾器是在 HttpServlet 以前。 過濾器攔截器運行前後步驟