使用springboot aop記錄接口請求的參數(shù)及響應
概述
使用aop做日志記錄,記錄輸入的參數(shù)名及參數(shù)值,并且記錄接口響應結果。
切面類
package com.zou.metabox.common.aspect; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; import java.util.stream.IntStream; /** * @author BIGSHU0923 * @description com.zou.metabox 中Controller層的的日志切面 * @since 7/30/2023 5:32 PM */ @Aspect @Component @Slf4j public class LoggingAspect { /** * com.zou.metabox.controller 包中公共方法的切入點 */ @Pointcut("execution(public * com.zou.metabox.controller.*.*(..))") public void loggingPointcut(){ // 暫不用處理 } /** 在日志切入點之前執(zhí)行的通知。 記錄方法名稱和請求參數(shù)。 @param joinPoint 控制器類中的連接點 */ @Before("loggingPointcut()") public void logBefore(JoinPoint joinPoint){ Object[] args = joinPoint.getArgs(); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); String[] parameterNames = methodSignature.getParameterNames(); Map<String, Object> requestMap = new HashMap<>(); IntStream.range(0, parameterNames.length) .forEach(i -> requestMap.put(parameterNames[i], args[i])); log.info("Before method:{}|{}", joinPoint.getSignature().getName(), requestMap); } /** 在日志切入點之后和方法成功執(zhí)行后執(zhí)行的通知。 記錄方法名稱和返回結果。 @param joinPoint 控制器類中的連接點 @param result 方法的返回結果 */ @AfterReturning(pointcut = "execution(public * com.zou.metabox.controller.*.*(..))", returning = "result") public void logResponse(JoinPoint joinPoint, Object result){ log.info("Method response:{}|{}", joinPoint.getSignature().getName(), result.toString()); } }
注意
這個切面定義的切點是在@Pointcut這個注解中定義的。我這里定義的是Controller中所有的public方法。
@After和@Afterreturning的區(qū)別
@After和@AfterReturning是兩個不同的切面通知類型。
@After通知會在目標方法執(zhí)行之后觸發(fā),無論目標方法是否拋出異常。所以在@After通知中不能訪問目標方法的返回值。
@AfterReturning通知只在目標方法成功執(zhí)行并返回后觸發(fā),可以訪問目標方法的返回值。一般使用@AfterReturning通知來收集方法的執(zhí)行結果或進行日志記錄。
優(yōu)化
@Around注解可以更方便地控制代理鏈條的行為,具體是指通過@Around注解實現(xiàn)的方法既可以代替@Before和@AfterReturning注解,也可以控制何時進入、何時退出被代理的方法。在執(zhí)行目標方法之前,可以在@Around注解標注的方法中編寫一些邏輯來決定是否繼續(xù)執(zhí)行目標方法,還可以修改傳遞給目標方法的參數(shù)。在執(zhí)行完目標方法后,可以在@Around注解
標注的方法中對返回值進行處理或者拋出異常。
@Around可以控制何時進入、何時退出被代理的方法。具體是通過ProceedingJoinPoint參數(shù)調用proceed()方法來執(zhí)行目標方法。如下面的result = pjp.proceed();這里起始執(zhí)行的就是接口的方法。所以可以控制何時進入、何時退出被代理的方法。
@Around("loggingPointcut()") public Object around(ProceedingJoinPoint pjp) throws Throwable{ // 獲取類名 String className = pjp.getTarget().getClass().getTypeName(); // 獲取方法名 String methodName = pjp.getSignature().getName(); // 獲取參數(shù)名 String[] parameterNames = ((MethodSignature) pjp.getSignature()).getParameterNames(); Object result = null; // 獲取參數(shù)值 Object[] args = pjp.getArgs(); // 獲取請求 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); // 獲取請求的url String url = request.getRequestURL().toString(); // 請求參數(shù),以參數(shù)名和值為鍵值對 Map<String, Object> paramMap = new HashMap<>(); IntStream.range(0, parameterNames.length).forEach(i->paramMap.put(parameterNames[i], args[i])); // header參數(shù) Enumeration<String> headerNames = request.getHeaderNames(); Map<String, Object> headerMap = new HashMap<>(); while (headerNames.hasMoreElements()){ String headerName = headerNames.nextElement(); String headerValue = request.getHeader(headerName); headerMap.put(headerName, headerValue); } // 打印請求參數(shù),記錄起始時間 long start = System.currentTimeMillis(); log.info("請求| 請求接口:{} | 類名:{} | 方法:{} | header參數(shù):{} | 參數(shù):{} | 請求時間:{}", url, className, methodName, headerMap, paramMap, LocalDateTime.now()); try { result = pjp.proceed(); } catch (Exception e) { log.error("返回| 處理時間:{} 毫秒 | 返回結果 :{}", (System.currentTimeMillis() - start), "failed"); throw e; } // 獲取執(zhí)行完的時間 打印返回報文 log.info("返回| 處理時間:{} 毫秒 | 返回結果 :{}", (System.currentTimeMillis() - start), "success"); return result; }
總結
Spring AOP 是基于代理的 AOP 框架,提供了幾個不同的切面建言(advice)注解:@Before、@AfterReturning、@Around、@AfterThrowing 和 @After。這些注解分別表示在目標方法執(zhí)行的不同時間點進行增強處理。
具體來說:
@Before:表示在目標方法執(zhí)行前進行增強處理。
@AfterReturning:表示在目標方法執(zhí)行后,返回結果之后進行增強處理,可以訪問到方法的返回值。
@Around:表示在目標方法執(zhí)行前、執(zhí)行中和執(zhí)行后都可以進行增強處理,并且需要手動控制何時進入、何時退出被代理的方法。
@AfterThrowing:表示在目標方法拋出異常后進行增強處理。
@After:表示在目標方法執(zhí)行后,無論是否發(fā)生異常,都進行增強處理。相較于@AfterReturning,@After增強處理無法獲取到方法的返回值。
在實際使用時,@Before 和 @AfterReturning 主要用于記錄日志、記性權限控制等與目標方法無關的操作;@Around 則比較常用,因為它可以在目標方法的執(zhí)行前后進行增強處理,并且可以更方便地控制代理鏈條的行為;@AfterThrowing 和 @After 通常用于回收資源,如關閉數(shù)據庫連接等操作。
相關文章
解決@CachePut設置的key值無法與@CacheValue的值匹配問題
這篇文章主要介紹了解決@CachePut設置的key的值無法與@CacheValue的值匹配問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12spring中IOC控制反轉依賴注入和new對象的區(qū)別說明
這篇文章主要介紹了spring中IOC控制反轉依賴注入和new對象的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02SpringBoot?整合?ShardingSphere4.1.1實現(xiàn)分庫分表功能
ShardingSphere是一套開源的分布式數(shù)據庫中間件解決方案組成的生態(tài)圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(計劃中)這3款相互獨立的產品組成,本文給大家介紹SpringBoot?整合?ShardingSphere4.1.1實現(xiàn)分庫分表,感興趣的朋友一起看看吧2023-12-12Java在Map轉Json字符串時出現(xiàn)"\"轉義字符的解決辦法
當一個Map被轉成Json字符串后,被添加到另一個Map中,會出現(xiàn)被加上“\”轉義字符的情況,這個時候該如何解決呢,下面就來和小編一起了解一下2023-07-07