天天看點

過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

作者:Java架構巴啦啦

前言

本文适合有一定基礎的同學,在已有的認識基礎上對這四塊的知識做一個總體的對比。

本文基于SpringBoot項目進行講解,所有的代碼都是在已經搭好SpringBoot的基礎上進行編寫的,SpringBoot版本為2.7.3。

該項目所有的代碼都已經上傳到我的GitHub倉庫:github.com/stick-i/Fil…

過濾器Filter

簡介

  • 來自J2EE中的Servlet技術
  • 實作原理:基于servlet的函數回調實作
  • 隻可以擷取到請求中的request和response,無法擷取到響應方法的資訊
  • 可以攔截所有請求
  • 支援使用xml配置和注解配置
  • 應用場景:權限認證、敏感詞檢測、通路日志記錄等

使用方法

  1. 實作 Filter 接口,重寫 doFilter 方法;
  2. 放行請求時調用chain.doFilter()方法;
  3. 啟用該過濾器,有三種方式,一種是比較原始的xml配置,這我就不寫了,需要的同學請檢視其它人的文章。
  4. 第二種是使用注解 @WebFilter() ,并在啟動類上添加@ServletComponentScan注解使用。
  5. 第三種是直接使用@Component注解,這樣的話@WebFilter配置的路徑會失效,因為@WebFilter根本就沒生效,不信自己去試試。
  6. 下面我使用的是第二種方式,更靈活
  7. 設定攔截路徑,就是要攔截的那個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";
	}

}
複制代碼           

測試

發送請求

過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

檢視控制台,可以觀察到執行的順序是按照我們代碼中的前後順序來執行的。

過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

攔截器Interceptor

簡介

  • 來自Spring,不依賴于servlet容器,但依賴于Spring
  • 實作原理:通過反射機制,動态代理實作
  • 可以擷取到Spring中存在的Bean,通過注入的方式
  • 隻對action請求起作用,并可以擷取到action請求的上下文
  • 應用場景:通路日志、權限管理等(場景這塊感覺跟過濾器差不多)

使用方法

  1. 實作 HandlerInterceptor 接口 或 繼承 HandlerInterceptorAdapter 類,建議使用接口;
  2. 實作 preHandle 方法(在處理請求前運作),實作 postHandle 方法(在處理請求完畢後運作);
  3. 再建立一個類,繼承 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;
	}
複制代碼           

測試

發送請求

過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

檢視控制台

過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

ControllerAdvice

簡介

  • 來自Spring,依賴于Spring
  • 應用場景:全局異常處理(配合自定義異常效果更佳)、資料綁定、資料預處理
  • 可以使用注解@ControllerAdvice,實作 ResponseBodyAdvice、RequestBodyAdvice 等接口,用于web項目的傳回資料加強。
過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

使用方法

  1. 建立一個類,給類加上注解@RestControllerAdvice
  2. 寫一個方法,給方法加上注解 @ExceptionHandler(Exception.class) ,括号裡寫要攔截的異常,方法的參數也是這個異常或者這個異常的父類
  3. 在方法中寫對該異常的處理

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、ControllerAdvcie的使用對比、執行順序...

檢視控制台

過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

AOP

簡介

  • AOP是一種面向切面程式設計的實作,實作的方式還挺多的,有SpringAOP,也有AspectJ、CGLIB等
  • 實作原理:基于動态代理或靜态代理
  • 細粒度的攔截,可以擷取到切入方法的參數等上下文資料,配合注解使用非常的舒服
  • 應用場景:一般用于方法的加強,比如方法級别的權限控制、日志輸出、讀取寫入緩存、方法調用次數限制等

使用方法

  1. 建立一個類,給類加上@Aspect 和 @Component注解
  2. 定義切入點 @Pointcut() ,可以定義在注解上(使用注解的類或方法即切入點),也可以直接指定切入的範圍
  3. 根據需要定義 @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");
	}

}
複制代碼           

最左邊會有個小符号

過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

Controller代碼,添加一個新的方法

@GetMapping("/aop")
	public String getAop() {
		log.info("getAop");
		return "aop";
	}
複制代碼           

測試

發送請求

過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

檢視控制台

過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

完全沒問題

四者的執行順序

好,既然我們四個都寫完了,那就再加一點東西,讓他們四個同時處理到一個方法上,那我們就可以測試出四者的執行順序了。

添加了幾個方法,這裡直接給大家截圖了,需要的同學可以去倉庫拿代碼。

Controller代碼

過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

AOP代碼

過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

Filter配置加了一條

過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

Interceptor配置也加一條

過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

最後Advice是不用加的

準備完畢,然後就可以去測試了。

發送請求:

過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

控制台:

過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

結論

可以看到,執行順序是:Filter過濾器 > Interceptor攔截器 > ControllerAdvice > AOP。

解釋:Filter和Interceptor的執行順序是可以直接看出來的,AOP、ControllerAdvice 的執行順序得看getAll的後面,getAll是controller輸出的内容嘛,它的下一條是aop,然後才是異常被捕獲,反方向先執行,說明 ControllerAdvice 是在 AOP 外面一層的。

這裡也可以看出來,當抛出的異常被 ControllerAdvice 捕獲之後, Interceptor 攔截器不會再有後置處理了,但是Filter過濾器還是有後置處理的。

這裡畫了張圖便于大家了解:

過濾器、攔截器、AOP、ControllerAdvcie的使用對比、執行順序...

總結

這四者其實都是面向切面程式設計思想的一種實作。具體使用選擇的話建議根據項目需要,參考它們的特性及執行順序,選擇最合适的一個或多個進行使用。

作者:阿杆

連結:https://juejin.cn/post/7153055158610427934

繼續閱讀