在SpringBoot項目中實現切面執行鏈功能

1.定義切面執行鏈頂級接口 AspectHandler

/*** 切面執行鏈**/
public interface AspectHandler {/*** 設置排除項* @param excludes*/default void setExcludes(List<String> excludes) {}/*** 獲取排除項* @return*/default List<String> getExcludes() {return new ArrayList<>();}/*** 前置處理* @param pjp* @return* @throws Exception*/boolean execute(ProceedingJoinPoint pjp) throws Exception;/*** 后置處理* @param pjp* @param response* @param exception*/default void afterCompletion(ProceedingJoinPoint pjp, Object response, Exception exception) {}
}

2.定義切面處理順序注解 AspectHandlerOrder

/*** 處理器排序*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AspectHandlerOrder {/*** The order value.* <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.** @see Ordered#getOrder()*/int value() default Ordered.LOWEST_PRECEDENCE;
}

3.web層統一處理切面類 AbstractWebAspect

/*** web層的統一切面,需要在使用時進行統一配置,設置切點*/
public abstract class AbstractWebAspect implements InitializingBean {private static final Logger logger = LoggerFactory.getLogger(AbstractWebAspect.class);private final static String SESSION_KEY = "sessionId";@Autowired(required = false)@Qualifier("apiAspectHandlers")private List<AspectHandler> aspectHandlers;private AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic void afterPropertiesSet() throws Exception {if (!CollectionUtils.isEmpty(aspectHandlers)) {Collections.sort(aspectHandlers, (o1, o2) -> {AspectHandlerOrder o1HandlerOrder = o1.getClass().getAnnotation(AspectHandlerOrder.class);AspectHandlerOrder o2HandlerOrder = o2.getClass().getAnnotation(AspectHandlerOrder.class);int o1OrderValue = o1HandlerOrder == null ? Ordered.LOWEST_PRECEDENCE : o1HandlerOrder.value();int o2OrderValue = o2HandlerOrder == null ? Ordered.LOWEST_PRECEDENCE : o2HandlerOrder.value();return o1OrderValue == o2OrderValue ? 0 : o1OrderValue > o2OrderValue ? 1 : -1;});}}public List<AspectHandler> getAspectHandlers() {return aspectHandlers;}private List<Class<? extends AspectHandler>> asList(Class<? extends AspectHandler>[] classes) {if (classes != null && classes.length > 0) {List<Class<? extends AspectHandler>> list = new ArrayList<>();for (Class<? extends AspectHandler> aClass : classes) {list.add(aClass);}return list;}return new ArrayList<>();}protected Object doAroundMethod(ProceedingJoinPoint pjp, List<AspectHandler> aspectHandlers) throws Exception {StopWatch stopWatch = new StopWatch();stopWatch.start();Exception exception = null;Object response = null;ArrayList<AspectHandler> executedAspectHandlers = new ArrayList<>();String methodName = pjp.toShortString();try {Object[] args = pjp.getArgs();logger.info("Aspect request={},args={}", methodName, Arrays.toString(args));// whether declare with AspectHandlerExclude annotation// if true pass declare excludes, default empty pass all// refactorAnnotation clazzAnnotation = getClazzAnnotation(pjp, AspectHandlerExclude.class);Annotation methodAnnotation = getMethodAnnotation(pjp, AspectHandlerExclude.class);boolean result = true;Class<? extends AspectHandler>[] methodExcludes = methodAnnotation == null ? new Class[]{} : ((AspectHandlerExclude) methodAnnotation).excludes().length == 0 ? aspectHandlers.stream().map(a -> a.getClass()).collect(Collectors.toList()).toArray(new Class[]{}) : ((AspectHandlerExclude) methodAnnotation).excludes();Class<? extends AspectHandler>[] clazzExcludes = clazzAnnotation == null ? new Class[]{} : ((AspectHandlerExclude) clazzAnnotation).excludes().length == 0 ? aspectHandlers.stream().map(a -> a.getClass()).collect(Collectors.toList()).toArray(new Class[]{}) : ((AspectHandlerExclude) clazzAnnotation).excludes();if (!CollectionUtils.isEmpty(aspectHandlers)) {aspectHandlers = new ArrayList<>(aspectHandlers);if (clazzExcludes.length > 0) {List<Class<? extends AspectHandler>> classes = asList(clazzExcludes);aspectHandlers.removeIf(h -> classes.contains(h.getClass()));}if (methodExcludes.length > 0) {List<Class<? extends AspectHandler>> classes = asList(methodExcludes);aspectHandlers.removeIf(h -> classes.contains(h.getClass()));}for (AspectHandler aspectHandler : aspectHandlers) {executedAspectHandlers.add(aspectHandler);result = aspectHandler.execute(pjp);if (!result) {break;}}}if (result) {response = pjp.proceed();if (response != null && response instanceof ApiResponse) {ApiResponse re = (ApiResponse) response;String sessionId = MDC.get(SESSION_KEY);re.setReqId(sessionId);return re;}return response;}} catch (Throwable throwable) {exception = (Exception) throwable;logger.error("execute:[{}],throw exception:[{}],message:[{}]", methodName, exception == null ? "" : exception.getClass().getName(), exception == null ? "" : exception.getMessage());throw exception;} finally {if (executedAspectHandlers.size() > 0) {for (int i = executedAspectHandlers.size() - 1; i >= 0; i--) {AspectHandler ah = executedAspectHandlers.get(i);try {ah.afterCompletion(pjp, response, exception);} catch (Exception e) {logger.error("AspectHandler afterCompletion execute error", e);}}}stopWatch.stop();long elapseTime = stopWatch.getTotalTimeMillis();logger.info("Aspect elapseTime:[{}],methodName:[{}]", elapseTime, methodName);}return response;}private void populateReqId(Object response) {}protected Object doAroundMethod(ProceedingJoinPoint pjp) throws Exception {return doAroundMethod(pjp, this.aspectHandlers);}private boolean matchExclude(AspectHandler aspectHandler, Class<? extends AspectHandler>[] excludes) {for (Class<? extends AspectHandler> exclude : excludes) {if (aspectHandler.getClass().equals(exclude)) {return true;}}HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String url = request.getRequestURI();List<String> excludeUrls = aspectHandler.getExcludes();if (!CollectionUtils.isEmpty(excludeUrls)) {for (String excludeUrl : excludeUrls) {if (antPathMatcher.match(excludeUrl, url)) {return true;}}}return false;}/*** get the annotation declare with method<br/>* if declare return the annotation,otherwise return null** @param pjp* @param annotation the annotation which find* @return*/private Annotation getMethodAnnotation(ProceedingJoinPoint pjp, Class<? extends Annotation> annotation) {Signature signature = pjp.getSignature();if (signature instanceof MethodSignature) {MethodSignature methodSignature = (MethodSignature) signature;return methodSignature.getMethod().getAnnotation(annotation);}return null;}/*** get the annotation declare with class<br/>* if declare return the annotation,otherwise return null** @param pjp* @param annotation the annotation which find* @return*/private Annotation getClazzAnnotation(ProceedingJoinPoint pjp, Class<? extends Annotation> annotation) {return pjp.getTarget().getClass().getAnnotation(annotation);}
}

4.切面配置類 AspectConfiguration

@Configuration
public class AspectConfiguration {@Beanpublic List<AspectHandler> apiAspectHandlers() {return Arrays.asList(new HttpHeaderAspectHandler(),new SecurityAspectHandler());}
}

5.定義在Http請求頭中添加自定義信息切面 HttpHeaderAspectHandler

@Slf4j
@AspectHandlerOrder(Ordered.HIGHEST_PRECEDENCE)
public class HttpHeaderAspectHandler implements AspectHandler {@Overridepublic boolean execute(ProceedingJoinPoint proceedingJoinPoint) {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();Enumeration<String> headerNames = request.getHeaderNames();HttpHeader httpHeader = new HttpHeader();while (headerNames.hasMoreElements()) {String s = headerNames.nextElement();String header = request.getHeader(s);Field field = ReflectionUtils.findField(HttpHeader.class, s);if (field != null) {field.setAccessible(true);ReflectionUtils.setField(field, httpHeader, header);}}//沒有按要求返回的情況,賦值默認值if(ObjectUtils.isEmpty(AppSourceEnum.self(httpHeader.getApp_source()))){httpHeader.setApp_source(AppSourceEnum.DEFAULT.getCode().toString());}WebContext.setHttpHeader(httpHeader);return true;}@Overridepublic void afterCompletion(ProceedingJoinPoint pjp, Object response, Exception exception) {AspectHandler.super.afterCompletion(pjp, response, exception);WebContext.removeHttpHeader();if (!(response instanceof ApiResponse)) {Optional.ofNullable(response).ifPresent((r) -> {log.error("controller return wrong type:" + response.getClass().getSimpleName());});return;}}
}

6.定義跳過切面注解 AspectHandlerExclude

/*** web controller中的方法加上該注解則會跳過切面邏輯**/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AspectHandlerExclude {/*** 如果excludes為空則跳過所有handler如果不為空則跳過指定的handler** @return*/Class<? extends AspectHandler>[] excludes() default {};
}

7.在controller方法上添加跳過切面配置

@AspectHandlerExclude(excludes = { SecurityAspectHandler.class })
public ApiResponse<?> findPageList(searchRequestDTO searchRequestDTO) {}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/19487.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/19487.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/19487.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

事務與并發控制

事務&#xff08;Transaction0&#xff09;&#xff1a;要么全做&#xff0c;要么全不做&#xff1b; 事務ACID&#xff1a;原子性Atomicity&#xff1b;一致性Consistency&#xff1b;隔離性Isolation&#xff1b;持久性Durability&#xff1b; 并發操作問題&#xff1a; 1.…

基于RNN和Transformer的詞級語言建模 代碼分析 _generate_square_subsequent_mask

基于RNN和Transformer的詞級語言建模 代碼分析 _generate_square_subsequent_mask flyfish Word-level Language Modeling using RNN and Transformer word_language_model PyTorch 提供的 word_language_model 示例展示了如何使用循環神經網絡RNN(GRU或LSTM)和 Transforme…

汽車IVI中控開發入門及進階(二十二):video decoder視頻解碼芯片

前言: 視頻解碼器在許多汽車、專業和消費視頻應用中仍有需求。Analog Devices是模擬視頻產品領域的行業領導者,提供一系列視頻解碼器,可將標準(SD,standard definition)和高清(HD,High definition)分辨率的模擬視頻高質量轉換為MIPI或TTL格式的數字視頻數據。典型的應…

【AI大模型】如何讓大模型變得更聰明?基于時代背景的思考

【AI大模型】如何讓大模型變得更聰明 前言 在以前&#xff0c;AI和大模型實際上界限較為清晰。但是隨著人工智能技術的不斷發展&#xff0c;基于大規模預訓練模型的應用在基于AI人工智能的技術支持和幫助上&#xff0c;多個領域展現出了前所未有的能力。無論是自然語言處理、…

算法刷題筆記 差分矩陣(C++實現)

文章目錄 題目前言題目描述解題思路和代碼實現 題目前言 這道題是一道差分算法的拓展題型&#xff0c;是算法刷題筆記到目前為止我認為最困難的題目之一。因此&#xff0c;這篇題解博客的過程記錄也最為詳細&#xff0c;希望能夠為你帶來幫助。 題目描述 輸入一個n行m列的整…

JavaScript的垃圾回收機制

No.內容鏈接1Openlayers 【入門教程】 - 【源代碼示例300】 2Leaflet 【入門教程】 - 【源代碼圖文示例 150】 3Cesium 【入門教程】 - 【源代碼圖文示例200】 4MapboxGL【入門教程】 - 【源代碼圖文示例150】 5前端就業寶典 【面試題詳細答案 1000】 文章目錄 一、垃圾…

匹配字符串

自學python如何成為大佬(目錄):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 Python提供了re模塊&#xff0c;用于實現正則表達式的操作。在實現時&#xff0c;可以使用re模塊提供的方法&#xff08;如search()、match()、finda…

深入理解Redis:多種操作方式詳解

Redis&#xff08;Remote Dictionary Server&#xff09;是一款高性能的開源鍵值存儲系統&#xff0c;廣泛應用于緩存、會話管理、實時分析等領域。它支持多種數據結構&#xff0c;如字符串、哈希、列表、集合和有序集合等&#xff0c;提供了豐富的操作命令。本篇博客將詳細介紹…

信息系統項目管理師0603:項目整合管理 — 考點總結(可直接理解記憶)

點擊查看專欄目錄 文章目錄 項目整合管理 — 考點總結(可直接理解記憶) 輸入、輸出、工具和技術 歷年考題直接考輸入,輸出、工具和技術的有17年11月第34、35,19年5月第34、35,20年11月27、28,21年5月第26,28,21年11月第28,22年5月第25,22年11月第22考題 項目章程是正…

CasaOS玩客云安裝全平臺高速下載器Gopeed并實現遠程訪問

&#x1f49d;&#x1f49d;&#x1f49d;歡迎來到我的博客&#xff0c;很高興能夠在這里和您見面&#xff01;希望您在這里可以感受到一份輕松愉快的氛圍&#xff0c;不僅可以獲得有趣的內容和知識&#xff0c;也可以暢所欲言、分享您的想法和見解。 推薦:kwan 的首頁,持續學…

BufferQueue 的工作原理

bufferQueue 是 Android 圖形棧中的一個核心組件,它在生產者和消費者之間傳遞緩沖區(buffer)。它通常用于圖形緩沖區管理,特別是在 SurfaceFlinger 和其他圖形相關的組件中。理解 BufferQueue 的工作原理對開發高性能圖形應用和解決圖形渲染問題非常有幫助。 BufferQueue …

基于Python的酒店客房入侵檢測系統的設計與實現

基于Python的酒店客房入侵檢測系統的設計與實現 開發語言:Python 數據庫&#xff1a;MySQL所用到的知識&#xff1a;Django框架工具&#xff1a;pycharm、Navicat、Maven 系統功能實現 酒店客房入侵管理界面 結合上文的結構搭建和用戶需求&#xff0c;酒店客房入侵檢測系統的…

【Unity Shader入門精要 第12章】屏幕后處理效果(一)

1. 原理和過程 屏幕后處理是綁定攝像機的&#xff0c;通過抓取當前攝像機渲染的圖像作為 SrcTextrue&#xff0c;然后按需依次調用處理接口&#xff0c;對 SrcTexture 進行處理&#xff0c;最后將處理完成的 DstTexture 顯示到屏幕上&#xff0c;整個過程的調度通過 C# 腳本完…

使用 C++ 在當前進程中獲取指定模塊的基址

C 實現 , 獲取指定模塊在該進程中的基址 1、流程: 獲取進程的所有模塊信息–>遍歷模塊列表 2、實現&#xff1a; // 我自己定義的 typedef struct moudle_date_ {HANDLE mhandle; // 句柄char mname[64]; // 名稱char* date; // 數據DWORD mdword; // 基址…

【機器學習】Adaboost: 強化弱學習器的自適應提升方法

&#x1f308;個人主頁: 鑫寶Code &#x1f525;熱門專欄: 閑話雜談&#xff5c; 炫酷HTML | JavaScript基礎 ?&#x1f4ab;個人格言: "如無必要&#xff0c;勿增實體" 文章目錄 Adaboost: 強化弱學習器的自適應提升方法引言Adaboost基礎概念弱學習器與強學習…

存儲器容量小才使用SRAM芯片,容量較大時使用DRAM芯片。為什么?

在計算機系統中&#xff0c;存儲器容量的選擇涉及到多種因素&#xff0c;包括成本、速度和復雜性。SRAM&#xff08;靜態隨機存取存儲器&#xff09;和DRAM&#xff08;動態隨機存取存儲器&#xff09;是兩種常見的內存類型&#xff0c;它們在設計和應用上有顯著的不同。以下是…

【藍橋杯嵌入式】 第六屆國賽

目錄 題目 配置 注意事項 代碼 - 默寫大師 EEPROM讀寫函數 LED驅動函數 ADC采集 上電初始化 LCD 按鍵 PWM互補輸出 全部代碼 hardware.c hardware.h control.c control.h main.c 題目 配置 注意事項 復制LCD的工程&#xff0c;先配置資源 --- 勾選完選項一…

CCIG 2024:合合信息文檔解析技術突破與應用前景

目錄 背景當前大模型訓練和應用面臨的問題訓練Token耗盡訓練語料質量要求高LLM文檔問答應用中文檔解析不精準 合合信息的文檔解析技術1. 具備多文檔元素識別能力2. 具備版面分析能力3. 高性能的文檔解析4. 高精準、高效率的文檔解析文檔多板式部分示例 文檔解析典型技術難點元素…

【代碼隨想錄Day23】|669.修建二叉搜索樹、108.將有序數組轉換為二叉搜索樹、538.把二叉搜索樹轉換為累加樹

669. 修剪二叉搜索樹 這題最開始的想法是復用刪除節點的那題的思路做&#xff0c;需要修改的部分就是要讓程序刪除完一個點后繼續遍歷&#xff0c;因為后續可能還有不符合條件的節點。但這樣想也做復雜了。 這類題其實不用想用什么序遍歷&#xff0c;用哪種方式只是為了更好的…

案例|開發一個美業小程序,都有什么功能

隨著移動互聯網的迅猛發展&#xff0c;美業連鎖機構紛紛尋求數字化轉型&#xff0c;以小程序為載體&#xff0c;提升服務效率&#xff0c;增強客戶體驗。 線下店現在面臨的困境&#xff1a; 客戶到店排隊時間過長&#xff0c;體驗感受差 新客引流難&#xff0c;老用戶回頭客…