使用springboot aop記錄接口請(qǐng)求的參數(shù)及響應(yīng)
概述
使用aop做日志記錄,記錄輸入的參數(shù)名及參數(shù)值,并且記錄接口響應(yīng)結(jié)果。
切面類(lèi)
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 包中公共方法的切入點(diǎn)
*/
@Pointcut("execution(public * com.zou.metabox.controller.*.*(..))")
public void loggingPointcut(){
// 暫不用處理
}
/**
在日志切入點(diǎn)之前執(zhí)行的通知。
記錄方法名稱(chēng)和請(qǐng)求參數(shù)。
@param joinPoint 控制器類(lèi)中的連接點(diǎn)
*/
@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);
}
/**
在日志切入點(diǎn)之后和方法成功執(zhí)行后執(zhí)行的通知。
記錄方法名稱(chēng)和返回結(jié)果。
@param joinPoint 控制器類(lèi)中的連接點(diǎn)
@param result 方法的返回結(jié)果
*/
@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());
}
}
注意
這個(gè)切面定義的切點(diǎn)是在@Pointcut這個(gè)注解中定義的。我這里定義的是Controller中所有的public方法。
@After和@Afterreturning的區(qū)別
@After和@AfterReturning是兩個(gè)不同的切面通知類(lèi)型。
@After通知會(huì)在目標(biāo)方法執(zhí)行之后觸發(fā),無(wú)論目標(biāo)方法是否拋出異常。所以在@After通知中不能訪問(wèn)目標(biāo)方法的返回值。
@AfterReturning通知只在目標(biāo)方法成功執(zhí)行并返回后觸發(fā),可以訪問(wèn)目標(biāo)方法的返回值。一般使用@AfterReturning通知來(lái)收集方法的執(zhí)行結(jié)果或進(jìn)行日志記錄。
優(yōu)化
@Around注解可以更方便地控制代理鏈條的行為,具體是指通過(guò)@Around注解實(shí)現(xiàn)的方法既可以代替@Before和@AfterReturning注解,也可以控制何時(shí)進(jìn)入、何時(shí)退出被代理的方法。在執(zhí)行目標(biāo)方法之前,可以在@Around注解標(biāo)注的方法中編寫(xiě)一些邏輯來(lái)決定是否繼續(xù)執(zhí)行目標(biāo)方法,還可以修改傳遞給目標(biāo)方法的參數(shù)。在執(zhí)行完目標(biāo)方法后,可以在@Around注解
標(biāo)注的方法中對(duì)返回值進(jìn)行處理或者拋出異常。
@Around可以控制何時(shí)進(jìn)入、何時(shí)退出被代理的方法。具體是通過(guò)ProceedingJoinPoint參數(shù)調(diào)用proceed()方法來(lái)執(zhí)行目標(biāo)方法。如下面的result = pjp.proceed();這里起始執(zhí)行的就是接口的方法。所以可以控制何時(shí)進(jìn)入、何時(shí)退出被代理的方法。
@Around("loggingPointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
// 獲取類(lèi)名
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();
// 獲取請(qǐng)求
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// 獲取請(qǐng)求的url
String url = request.getRequestURL().toString();
// 請(qǐng)求參數(shù),以參數(shù)名和值為鍵值對(duì)
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);
}
// 打印請(qǐng)求參數(shù),記錄起始時(shí)間
long start = System.currentTimeMillis();
log.info("請(qǐng)求| 請(qǐng)求接口:{} | 類(lèi)名:{} | 方法:{} | header參數(shù):{} | 參數(shù):{} | 請(qǐng)求時(shí)間:{}", url, className, methodName, headerMap, paramMap, LocalDateTime.now());
try {
result = pjp.proceed();
} catch (Exception e) {
log.error("返回| 處理時(shí)間:{} 毫秒 | 返回結(jié)果 :{}", (System.currentTimeMillis() - start), "failed");
throw e;
}
// 獲取執(zhí)行完的時(shí)間 打印返回報(bào)文
log.info("返回| 處理時(shí)間:{} 毫秒 | 返回結(jié)果 :{}", (System.currentTimeMillis() - start), "success");
return result;
}
總結(jié)
Spring AOP 是基于代理的 AOP 框架,提供了幾個(gè)不同的切面建言(advice)注解:@Before、@AfterReturning、@Around、@AfterThrowing 和 @After。這些注解分別表示在目標(biāo)方法執(zhí)行的不同時(shí)間點(diǎn)進(jìn)行增強(qiáng)處理。
具體來(lái)說(shuō):
@Before:表示在目標(biāo)方法執(zhí)行前進(jìn)行增強(qiáng)處理。
@AfterReturning:表示在目標(biāo)方法執(zhí)行后,返回結(jié)果之后進(jìn)行增強(qiáng)處理,可以訪問(wèn)到方法的返回值。
@Around:表示在目標(biāo)方法執(zhí)行前、執(zhí)行中和執(zhí)行后都可以進(jìn)行增強(qiáng)處理,并且需要手動(dòng)控制何時(shí)進(jìn)入、何時(shí)退出被代理的方法。
@AfterThrowing:表示在目標(biāo)方法拋出異常后進(jìn)行增強(qiáng)處理。
@After:表示在目標(biāo)方法執(zhí)行后,無(wú)論是否發(fā)生異常,都進(jìn)行增強(qiáng)處理。相較于@AfterReturning,@After增強(qiáng)處理無(wú)法獲取到方法的返回值。
在實(shí)際使用時(shí),@Before 和 @AfterReturning 主要用于記錄日志、記性權(quán)限控制等與目標(biāo)方法無(wú)關(guān)的操作;@Around 則比較常用,因?yàn)樗梢栽谀繕?biāo)方法的執(zhí)行前后進(jìn)行增強(qiáng)處理,并且可以更方便地控制代理鏈條的行為;@AfterThrowing 和 @After 通常用于回收資源,如關(guān)閉數(shù)據(jù)庫(kù)連接等操作。
相關(guān)文章
解決@CachePut設(shè)置的key值無(wú)法與@CacheValue的值匹配問(wèn)題
這篇文章主要介紹了解決@CachePut設(shè)置的key的值無(wú)法與@CacheValue的值匹配問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
spring中IOC控制反轉(zhuǎn)依賴注入和new對(duì)象的區(qū)別說(shuō)明
這篇文章主要介紹了spring中IOC控制反轉(zhuǎn)依賴注入和new對(duì)象的區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
Struts2學(xué)習(xí)教程之輸入校驗(yàn)示例詳解
這篇文章主要給大家介紹了關(guān)于Struts2學(xué)習(xí)教程之輸入校驗(yàn)的相關(guān)資料,文中通過(guò)示例介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用struts2具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05
SpringBoot?整合?ShardingSphere4.1.1實(shí)現(xiàn)分庫(kù)分表功能
ShardingSphere是一套開(kāi)源的分布式數(shù)據(jù)庫(kù)中間件解決方案組成的生態(tài)圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(計(jì)劃中)這3款相互獨(dú)立的產(chǎn)品組成,本文給大家介紹SpringBoot?整合?ShardingSphere4.1.1實(shí)現(xiàn)分庫(kù)分表,感興趣的朋友一起看看吧2023-12-12
Spring Boot開(kāi)啟遠(yuǎn)程調(diào)試的方法
這篇文章主要介紹了Spring Boot開(kāi)啟遠(yuǎn)程調(diào)試的方法,幫助大家更好的理解和使用Spring Boot框架,感興趣的朋友可以了解下2020-10-10
Java在Map轉(zhuǎn)Json字符串時(shí)出現(xiàn)"\"轉(zhuǎn)義字符的解決辦法
當(dāng)一個(gè)Map被轉(zhuǎn)成Json字符串后,被添加到另一個(gè)Map中,會(huì)出現(xiàn)被加上“\”轉(zhuǎn)義字符的情況,這個(gè)時(shí)候該如何解決呢,下面就來(lái)和小編一起了解一下2023-07-07
springboot啟動(dòng)類(lèi)如何剔除掃描某個(gè)包
這篇文章主要介紹了springboot啟動(dòng)類(lèi)如何剔除掃描某個(gè)包,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11

