前言
本文适合有一定基礎的同學,在已有的認識基礎上對這四塊的知識做一個總體的對比。
本文基于SpringBoot項目進行講解,所有的代碼都是在已經搭好SpringBoot的基礎上進行編寫的,SpringBoot版本為2.7.3。
該項目所有的代碼都已經上傳到我的GitHub倉庫:github.com/stick-i/Fil…
過濾器Filter
簡介
- 來自J2EE中的Servlet技術
- 實作原理:基于servlet的函數回調實作
- 隻可以擷取到請求中的request和response,無法擷取到響應方法的資訊
- 可以攔截所有請求
- 支援使用xml配置和注解配置
- 應用場景:權限認證、敏感詞檢測、通路日志記錄等
使用方法
- 實作 Filter 接口,重寫 doFilter 方法;
- 放行請求時調用chain.doFilter()方法;
- 啟用該過濾器,有三種方式,一種是比較原始的xml配置,這我就不寫了,需要的同學請檢視其它人的文章。
- 第二種是使用注解 @WebFilter() ,并在啟動類上添加@ServletComponentScan注解使用。
- 第三種是直接使用@Component注解,這樣的話@WebFilter配置的路徑會失效,因為@WebFilter根本就沒生效,不信自己去試試。
- 下面我使用的是第二種方式,更靈活
- 設定攔截路徑,就是要攔截的那個url路徑。
代碼實作
過濾器代碼
@Slf4j
@WebFilter(value = "/name") //這裡我們隻攔截name請求,記得要在啟動類配ServletComponentScan
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
log.info("before filter");
// 請求放行
chain.doFilter(request, response);
log.info("after filter");
}
}
複制代碼
Controller代碼
@Slf4j
@RestController
public class MyController {
@GetMapping("/name")
public String getName() {
log.info("getName");
return "sticki";
}
}
複制代碼
測試
發送請求
檢視控制台,可以觀察到執行的順序是按照我們代碼中的前後順序來執行的。
攔截器Interceptor
簡介
- 來自Spring,不依賴于servlet容器,但依賴于Spring
- 實作原理:通過反射機制,動态代理實作
- 可以擷取到Spring中存在的Bean,通過注入的方式
- 隻對action請求起作用,并可以擷取到action請求的上下文
- 應用場景:通路日志、權限管理等(場景這塊感覺跟過濾器差不多)
使用方法
- 實作 HandlerInterceptor 接口 或 繼承 HandlerInterceptorAdapter 類,建議使用接口;
- 實作 preHandle 方法(在處理請求前運作),實作 postHandle 方法(在處理請求完畢後運作);
- 再建立一個類,繼承 WebMvcConfigurer 接口,再實作addInterceptors方法,并在方法中注冊該攔截器、配置攔截路徑(不配置預設攔截所有請求);
代碼實作
攔截器代碼
@Slf4j
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
log.info("before interceptor");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
log.info("after interceptor");
}
}
複制代碼
配置類代碼
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 隻攔截 age 的請求
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/age");
}
}
複制代碼
Controller代碼,還是剛剛那個Controller,不過我添加了一個新的請求 /age
@GetMapping("/age")
public Integer getAge() {
log.info("getAge");
return 22;
}
複制代碼
測試
發送請求
檢視控制台
ControllerAdvice
簡介
- 來自Spring,依賴于Spring
- 應用場景:全局異常處理(配合自定義異常效果更佳)、資料綁定、資料預處理
- 可以使用注解@ControllerAdvice,實作 ResponseBodyAdvice、RequestBodyAdvice 等接口,用于web項目的傳回資料加強。
使用方法
- 建立一個類,給類加上注解@RestControllerAdvice
- 寫一個方法,給方法加上注解 @ExceptionHandler(Exception.class) ,括号裡寫要攔截的異常,方法的參數也是這個異常或者這個異常的父類
- 在方法中寫對該異常的處理
ps:這裡我隻寫了全局異常處理的代碼,需要的同學可以去查一查其他的使用方法噢,也可以看一下這個:www.cnblogs.com/tiancai/p/1… ,寫的挺全的。
代碼實作
ControllerAdvice
@Slf4j
@RestControllerAdvice
public class ExceptionAdvice {
@ExceptionHandler(Exception.class)
public String allExceptionHandler(Exception e) {
log.warn(e.getMessage()); // 捕獲之後列印異常資訊
return "系統異常,請稍後再試";
}
}
複制代碼
Controller代碼,也是新加了一個請求 /exception
@GetMapping("/exception")
public String getException() {
log.info("getException");
throw new RuntimeException("報錯啦");
}
複制代碼
測試
發送請求,可以看到這裡的資訊是傳回的 ControllerAdvice 裡面的,而不是Controller裡面的。
檢視控制台
AOP
簡介
- AOP是一種面向切面程式設計的實作,實作的方式還挺多的,有SpringAOP,也有AspectJ、CGLIB等
- 實作原理:基于動态代理或靜态代理
- 細粒度的攔截,可以擷取到切入方法的參數等上下文資料,配合注解使用非常的舒服
- 應用場景:一般用于方法的加強,比如方法級别的權限控制、日志輸出、讀取寫入緩存、方法調用次數限制等
使用方法
- 建立一個類,給類加上@Aspect 和 @Component注解
- 定義切入點 @Pointcut() ,可以定義在注解上(使用注解的類或方法即切入點),也可以直接指定切入的範圍
- 根據需要定義 @Before("pointcut()") 和 @After("pointcut()"),然後在内部寫處理邏輯
ps:這裡我隻寫了spring aop的使用,需要的同學自己去百度找其他的使用方法噢
代碼實作
AOP代碼
@Slf4j
@Aspect
@Component
public class MyAop {
/**
* 僅比對getAop這一個方法
*/
@Pointcut("execution(* cn.sticki.test.controller.MyController.getAop())")
public void pointcut() {
}
@Before("pointcut()")
public void before() {
log.info("aop before");
}
@After("pointcut()")
public void after() {
log.info("aop after");
}
}
複制代碼
最左邊會有個小符号
Controller代碼,添加一個新的方法
@GetMapping("/aop")
public String getAop() {
log.info("getAop");
return "aop";
}
複制代碼
測試
發送請求
檢視控制台
完全沒問題
四者的執行順序
好,既然我們四個都寫完了,那就再加一點東西,讓他們四個同時處理到一個方法上,那我們就可以測試出四者的執行順序了。
添加了幾個方法,這裡直接給大家截圖了,需要的同學可以去倉庫拿代碼。
Controller代碼
AOP代碼
Filter配置加了一條
Interceptor配置也加一條
最後Advice是不用加的
準備完畢,然後就可以去測試了。
發送請求:
控制台:
結論
可以看到,執行順序是:Filter過濾器 > Interceptor攔截器 > ControllerAdvice > AOP。
解釋:Filter和Interceptor的執行順序是可以直接看出來的,AOP、ControllerAdvice 的執行順序得看getAll的後面,getAll是controller輸出的内容嘛,它的下一條是aop,然後才是異常被捕獲,反方向先執行,說明 ControllerAdvice 是在 AOP 外面一層的。
這裡也可以看出來,當抛出的異常被 ControllerAdvice 捕獲之後, Interceptor 攔截器不會再有後置處理了,但是Filter過濾器還是有後置處理的。
這裡畫了張圖便于大家了解:
總結
這四者其實都是面向切面程式設計思想的一種實作。具體使用選擇的話建議根據項目需要,參考它們的特性及執行順序,選擇最合适的一個或多個進行使用。
作者:阿杆
連結:https://juejin.cn/post/7153055158610427934