SpringBoot 攔截器 (Interceptor)與切面 (AOP)示例、作用及適用場景分析
SpringBoot 攔截器 (Interceptor) 與切面 (AOP):示例、作用、及適用場景
一、攔截器 (Interceptor):SpringMVC 層的請求攔截器
攔截器是SpringMVC 框架的組件,基于Java 動態(tài)代理實現(xiàn),作用于Web 層(Controller 層),僅能攔截 HTTP 請求的處理過程,生命周期與 SpringMVC 的請求處理流程綁定。
核心作用
- 攔截 Controller 層的 HTTP 請求(如 GET/POST 請求);
- 實現(xiàn)請求前的預(yù)處理(如登錄校驗、token 驗證、跨域配置);
- 實現(xiàn)請求后的后置處理(如響應(yīng)數(shù)據(jù)統(tǒng)一封裝);
- 實現(xiàn)請求完成后的最終處理(如資源釋放、請求日志記錄)。
1.自定義攔截器:實現(xiàn)HandlerInterceptor接口
@Component
@Slf4j
public class CompanyContextInterceptor implements HandlerInterceptor {
@Resource
private RedisTemplate<String, Object> redisTemplate;
private static final String REDIS_TOKEN_KEY_PREFIX = "login:";
// 請求開始前:解析 Header,設(shè)置公司編碼到上下文
@Override
public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {
// 1. 從請求頭獲取Token(Bearer Token格式)
String authHeader = request.getHeader("Authorization");
if (StringUtils.isBlank(authHeader) || !authHeader.startsWith("Bearer ")) {
// 未攜帶Token,直接通過(或返回401,根據(jù)業(yè)務(wù)需求)
return true;
}
String token = authHeader.substring(7); // 截取Bearer后的Token
// 2. 從Redis查詢登錄用戶信息
String redisKey = REDIS_TOKEN_KEY_PREFIX + token;
LoginController.LoginWxUser loginWxUser = (LoginController.LoginWxUser) redisTemplate.opsForValue().get(redisKey);
if (loginWxUser == null || StringUtils.isBlank(loginWxUser.getPhone())) {
// Token無效/過期,直接通過(或返回401)
return true;
}
// 3. 核心:將loginWxUser存入request,key必須是「loginWxUser」(和接口一致?。?
request.setAttribute("loginWxUser", loginWxUser);
// 可選:Token續(xù)期
redisTemplate.expire(redisKey, 7, TimeUnit.DAYS);
// 獲取請求路徑
String requestURI = request.getRequestURI();
// 檢查是否為需要忽略的登錄接口
if (requestURI.equals("/workaffairs/api/user/loginByPhone") ||
requestURI.startsWith("/workaffairs/api/user/loginByPhone")) {
return true; // 直接放行,不設(shè)置公司編碼
}
// 從 Header 獲取公司編碼
String companyCode = request.getHeader("x-Company-Code");
if (companyCode != null && !companyCode.isEmpty()) {
CompanyContextHolder.setCompanyCode(companyCode);
log.info("獲取到公司編碼----" + companyCode);
}
return true; // 放行請求
}
// 請求結(jié)束后:清理上下文(必須執(zhí)行,避免內(nèi)存泄漏)
@Override
public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, Exception ex) throws Exception {
CompanyContextHolder.clear();
}
}2.攔截器配置類:實現(xiàn)WebMvcConfigurer接口
/**
* 攔截器配置類:注冊攔截器并配置攔截規(guī)則
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
// 注入自定義攔截器
@Resource
private CompanyContextInterceptor companyContextInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(companyContextInterceptor)
// 攔截所有需要切換數(shù)據(jù)源的業(yè)務(wù)接口(根據(jù)你的實際路徑調(diào)整)
.addPathPatterns("/purchase/**", "/delivery/**","/api/user/getSupplierByCompany")
// 排除不需要攔截的接口(比如登錄接口)
.excludePathPatterns("/api/user/loginByPhone");
}
}二、切面 (AOP):面向切面的通用橫切邏輯實現(xiàn)
核心概念
切面是Spring AOP 框架的核心,基于JDK 動態(tài)代理(針對接口)+CGLIB 動態(tài)代理(針對類) 實現(xiàn),是面向切面編程的落地方式,可作用于Spring 容器管理的所有 Bean(Controller、Service、Dao、普通組件等),無層級限制。
核心作用
- 提取通用橫切邏輯(與業(yè)務(wù)無關(guān)、可復(fù)用的邏輯),如日志記錄、事務(wù)管理、耗時統(tǒng)計、異常統(tǒng)一捕獲;
- 實現(xiàn)業(yè)務(wù)代碼與橫切邏輯的解耦,無需在業(yè)務(wù)方法中硬編碼通用邏輯;
- 可對任意方法進行增強(前置增強、后置增強、環(huán)繞增強、異常增強等),支持靈活的切點表達式配置。
1.自定義切面
@Aspect // 聲明為切面類
@Component // 交給Spring容器管理
@Slf4j
public class DbOperationLogAspect {
/**
* 定義切點:指定切面作用的目標方法
* 表達式規(guī)則:execution(訪問修飾符 返回值 包名.類名.方法名(參數(shù)類型))
* 示例:匹配com.tgerp.workaffairs.mapper包下所有類的所有方法
*/
@Pointcut("execution(* com.tgerp.workaffairs.mapper..*(..))")
public void dbOperationPointCut() {
}
@Around("dbOperationPointCut()")
public Object aroundDbOperation(ProceedingJoinPoint joinPoint) throws Throwable {
// 1. 此時controller已經(jīng)執(zhí)行了setDbName,能拿到正確的數(shù)據(jù)庫名
String currentDbName = DynamicDataSourceContextHolder.peek(); // 從數(shù)據(jù)源上下文拿
// }
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
// 打印日志
log.info("[workaffairs-數(shù)據(jù)庫: {}] 執(zhí)行數(shù)據(jù)庫操作 - 類:{},方法:{}", currentDbName, className, methodName);
try {
return joinPoint.proceed();
} catch (Exception e) {
log.error("[workaffairs-數(shù)據(jù)庫: {}] 數(shù)據(jù)庫操作失敗 - 類:{},方法:{},異常信息:{}",
currentDbName, className, methodName, e.getMessage(), e);
throw e;
}
}
}三、攔截器與切面的適用場景
選型核心原則:根據(jù)橫切邏輯的「作用范圍」和「是否與 Web 請求相關(guān)」判斷
攔截器 (Interceptor):僅適用于Web 相關(guān)的橫切邏輯
所有與 HTTP 請求強綁定的場景,優(yōu)先使用攔截器,因為可直接獲取 Web 上下文(Request/Response),無需額外處理:
- 登錄 / 權(quán)限校驗:基于請求頭 / 參數(shù)驗證 token、會話、用戶身份;
- 跨域配置:統(tǒng)一設(shè)置響應(yīng)頭(Access-Control-Allow-Origin 等);
- 請求參數(shù)預(yù)處理:統(tǒng)一解析請求頭、請求參數(shù),如防 XSS 處理;
- 響應(yīng)數(shù)據(jù)統(tǒng)一封裝:對 Controller 返回的結(jié)果統(tǒng)一添加 code/msg/data 格式;
- Web 層日志記錄:記錄請求 URI、請求方式、IP、響應(yīng)狀態(tài)等 Web 相關(guān)信息;
- 接口限流:基于 IP / 請求頻率限制 HTTP 請求(如每秒最大請求數(shù))。
切面 (AOP):適用于通用、跨層的橫切邏輯
所有與業(yè)務(wù)無關(guān)、需要在多層級復(fù)用的通用橫切邏輯,優(yōu)先使用切面,解耦效果最佳,無環(huán)境依賴:
- 全局日志記錄:記錄方法的入?yún)?、出參、?zhí)行耗時(適用于 Service/Dao 層);
- 事務(wù)管理:通過
@Transactional(底層基于 AOP)實現(xiàn)方法級事務(wù)控制; - 方法級權(quán)限校驗:基于自定義注解(如 @RequiresPermission)實現(xiàn)方法訪問控制;
- 異常統(tǒng)一捕獲:捕獲指定層的異常,統(tǒng)一封裝異常響應(yīng)(如 Service 層業(yè)務(wù)異常);
- 緩存控制:實現(xiàn)方法級緩存(如查詢方法結(jié)果緩存,避免重復(fù)查詢);
- 性能監(jiān)控:統(tǒng)計指定方法的執(zhí)行耗時、調(diào)用次數(shù),做系統(tǒng)性能分析;
- 非 Web 環(huán)境的橫切邏輯:如定時任務(wù)、后臺服務(wù)的方法增強(無 Web 上下文,攔截器無法使用)。
實際項目中,攔截器和切面并非互斥,而是協(xié)同工作,各自負責(zé)專屬領(lǐng)域的橫切邏輯,形成完整的橫切體系:
例如:一個接口的完整處理流程中:
- 攔截器:在請求進入 Controller 前,完成登錄校驗、token 驗證(Web 層專屬);
- 切面:在 Service 層方法執(zhí)行時,完成耗時統(tǒng)計、事務(wù)管理、異常捕獲(通用跨層);
- 攔截器:在請求完成后,完成響應(yīng)數(shù)據(jù)封裝、Web 日志記錄(Web 層專屬)。
這種協(xié)同模式既保證了 Web 相關(guān)邏輯的高效處理,又實現(xiàn)了通用邏輯的跨層復(fù)用,是 SpringBoot 項目的標準實踐。
到此這篇關(guān)于SpringBoot 攔截器 (Interceptor)與切面 (AOP)示例、作用及適用場景分析的文章就介紹到這了,更多相關(guān)SpringBoot 攔截器和切面內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- AOP在SpringBoot項目中的使用場景解讀
- SpringBoot整合Jasypt使用自定義注解+AOP實現(xiàn)敏感字段加解密
- Springboot如何正確使用AOP問題
- springboot接口服務(wù),防刷、防止請求攻擊,AOP實現(xiàn)方式
- SpringBoot3利用AOP實現(xiàn)IP黑名單功能
- springbootAOP定義切點獲取/修改請求參數(shù)方式
- SpringBoot實現(xiàn)AOP切面的三種方式
- SpringBoot中使用AOP實現(xiàn)日志記錄功能
- SpringBoot AOP如何配置全局事務(wù)
- JAVA中Spring Boot的AOP切面編程是什么,如何使用?(實例代碼)
相關(guān)文章
Java向kettle8.0傳遞參數(shù)的方式總結(jié)
介紹了如何在Kettle中傳遞參數(shù)到轉(zhuǎn)換和作業(yè)中,包括設(shè)置全局properties、使用TransMeta和JobMeta的parameterValue,以及通過EL表達式獲取參數(shù)值2025-01-01
一文教你利用Stream?API批量Mock數(shù)據(jù)的方法
在日常開發(fā)的過程中我們經(jīng)常會遇到需要mock一些數(shù)據(jù)的場景,比如說?mock?一些接口的返回或者說?mock?一些測試消息用于隊列生產(chǎn)者發(fā)送消息。本文將教你如何通過?Stream?API?批量?Mock?數(shù)據(jù),需要的可以參考一下2022-09-09
SpringBoot實現(xiàn)數(shù)據(jù)轉(zhuǎn)換的四種對象映射方案
在項目開發(fā)中,對象之間的相互轉(zhuǎn)換是一個高頻操作,尤其在分層架構(gòu)的系統(tǒng)中,數(shù)據(jù)在實體對象(Entity)、數(shù)據(jù)傳輸對象(DTO)、值對象(VO)之間的轉(zhuǎn)換尤為常見,選擇一個高效、可靠的對象映射方案對提高系統(tǒng)性能和開發(fā)效率至關(guān)重要,本文將介紹4種對象映射方案2025-06-06
解決springboot遇到autowire注入為null的問題
這篇文章主要介紹了解決springboot遇到autowire注入為null的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03
SpringBoot讀取Resource目錄文件的五種常見方式
在Spring Boot開發(fā)中,我們經(jīng)常需要讀取src/main/resources目錄下的文件,src/main/resources 目錄下通常存放配置文件、模板、靜態(tài)資源、SQL腳本等,本文給大家介紹了SpringBoot讀取Resource目錄文件的五種常見方式,需要的朋友可以參考下2025-07-07

