springboot配置aop切面日志打印過程解析
這篇文章主要介紹了springboot配置aop切面日志打印過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
一、SpringBoot Aop說明
1. Aop
AOP(Aspect-Oriented Programming,面向切面編程),它利用一種”橫切”的技術(shù),將那些多個類的共同行為封裝到一個可重用的模塊。便于減少系統(tǒng)的重復(fù)代碼,降低模塊之間的耦合度,并有利于未來的可操作性和可維護(hù)性。
2. AOP相關(guān)概念:
Aspect(切面):聲明類似于Java中的類聲明,在Aspect中會包含一些Pointcut及相應(yīng)的Advice。
Joint point(連接點(diǎn)):表示在程序中明確定義的點(diǎn)。包括方法的調(diào)用、對類成員的訪問等。
Pointcut(切入點(diǎn)):表示一個組Joint point,如方法名、參數(shù)類型、返回類型等等。
Advice(通知):Advice定義了在Pointcut里面定義的程序點(diǎn)具體要做的操作,它通過(before、around、after(return、throw)、finally來區(qū)別實(shí)在每個Joint point之前、之后還是執(zhí)行 前后要調(diào)用的代碼。
Before:在執(zhí)行方法前調(diào)用Advice,比如請求接口之前的登錄驗(yàn)證。
Around:在執(zhí)行方法前后調(diào)用Advice,這是最常用的方法。
After:在執(zhí)行方法后調(diào)用Advice,after、return是方法正常返回后調(diào)用,after\throw是方法拋出異常后調(diào)用。
Finally:方法調(diào)用后執(zhí)行Advice,無論是否拋出異常還是正常返回。
AOP proxy:AOP proxy也是Java對象,是由AOP框架創(chuàng)建,用來完成上述動作,AOP對象通??梢酝ㄟ^JDK dynamic proxy完成,或者使用CGLIb完成。
Weaving:實(shí)現(xiàn)上述切面編程的代碼織入,可以在編譯時刻,也可以在運(yùn)行時刻,Spring和其它大多數(shù)Java框架都是在運(yùn)行時刻生成代理。
二、代碼示例
1. POM引入
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 分析客戶端信息的工具類-->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>1.21</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>1.18.10</scope>
</dependency>
</dependencies>
2、切面:WebLogAspect代碼
/**
* 添加aop日志打印
*
* @author 馬振全 2020/1/13 14:42
*/
@Aspect
@Component
@Slf4j
public class WebLogAspect {
/**
* 進(jìn)入方法時間戳
*/
private Long startTime;
/**
* 方法結(jié)束時間戳(計(jì)時)
*/
private Long endTime;
public WebLogAspect() {
}
/**
* 定義請求日志切入點(diǎn),其切入點(diǎn)表達(dá)式有多種匹配方式,這里是指定路徑
*/
@Pointcut("execution(public * com.soyoung.ad.engine.controller.*.*(..))")
public void webLogPointcut() {
}
/**
* 前置通知:
* 1. 在執(zhí)行目標(biāo)方法之前執(zhí)行,比如請求接口之前的登錄驗(yàn)證;
* 2. 在前置通知中設(shè)置請求日志信息,如開始時間,請求參數(shù),注解內(nèi)容等
*
* @param joinPoint
* @throws Throwable
*/
@Before("webLogPointcut()")
public void doBefore(JoinPoint joinPoint) {
// 接收到請求,記錄請求內(nèi)容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//獲取請求頭中的User-Agent
UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
//打印請求的內(nèi)容
startTime = System.currentTimeMillis();
log.info("請求開始時間:{}", LocalDateTime.now());
log.info("請求Url : {}", request.getRequestURL().toString());
log.info("請求方式 : {}", request.getMethod());
log.info("請求ip : {}", request.getRemoteAddr());
log.info("請求方法 : ", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
log.info("請求參數(shù) : {}", Arrays.toString(joinPoint.getArgs()));
// 系統(tǒng)信息
log.info("瀏覽器:{}", userAgent.getBrowser().toString());
log.info("瀏覽器版本:{}", userAgent.getBrowserVersion());
log.info("操作系統(tǒng): {}", userAgent.getOperatingSystem().toString());
}
/**
* 返回通知:
* 1. 在目標(biāo)方法正常結(jié)束之后執(zhí)行
* 1. 在返回通知中補(bǔ)充請求日志信息,如返回時間,方法耗時,返回值,并且保存日志信息
*
* @param ret
* @throws Throwable
*/
@AfterReturning(returning = "ret", pointcut = "webLogPointcut()")
public void doAfterReturning(Object ret) throws Throwable {
endTime = System.currentTimeMillis();
log.info("請求結(jié)束時間:{}", LocalDateTime.now());
log.info("請求耗時:{}", (endTime - startTime));
// 處理完請求,返回內(nèi)容
log.info("請求返回 : {}", ret);
}
/**
* 異常通知:
* 1. 在目標(biāo)方法非正常結(jié)束,發(fā)生異?;蛘邟伋霎惓r執(zhí)行
* 1. 在異常通知中設(shè)置異常信息,并將其保存
*
* @param throwable
*/
@AfterThrowing(value = "webLogPointcut()", throwing = "throwable")
public void doAfterThrowing(Throwable throwable) {
// 保存異常日志記錄
log.error("發(fā)生異常時間:{}", LocalDateTime.now());
log.error("拋出異常:{}", throwable.getMessage());
}
}
3、@Before和@AfterReturning部分也可使用以下代碼替代
/**
* 在執(zhí)行方法前后調(diào)用Advice,這是最常用的方法,相當(dāng)于@Before和@AfterReturning全部做的事兒
* @param pjp
* @return
* @throws Throwable
*/
@Around("webLogPointcut()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
// 接收到請求,記錄請求內(nèi)容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//獲取請求頭中的User-Agent
UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
//打印請求的內(nèi)容
startTime = System.currentTimeMillis();
log.info("請求Url : {}" , request.getRequestURL().toString());
log.info("請求方式 : {}" , request.getMethod());
log.info("請求ip : {}" , request.getRemoteAddr());
log.info("請求方法 : " , pjp.getSignature().getDeclaringTypeName() , "." , pjp.getSignature().getName());
log.info("請求參數(shù) : {}" , Arrays.toString(pjp.getArgs()));
// 系統(tǒng)信息
log.info("瀏覽器:{}", userAgent.getBrowser().toString());
log.info("瀏覽器版本:{}",userAgent.getBrowserVersion());
log.info("操作系統(tǒng): {}", userAgent.getOperatingSystem().toString());
// pjp.proceed():當(dāng)我們執(zhí)行完切面代碼之后,還有繼續(xù)處理業(yè)務(wù)相關(guān)的代碼。proceed()方法會繼續(xù)執(zhí)行業(yè)務(wù)代碼,并且其返回值,就是業(yè)務(wù)處理完成之后的返回值。
Object ret = pjp.proceed();
log.info("請求結(jié)束時間:"+ LocalDateTime.now());
log.info("請求耗時:{}" , (System.currentTimeMillis() - startTime));
// 處理完請求,返回內(nèi)容
log.info("請求返回 : " , ret);
return ret;
}
4、測試結(jié)果
2020-01-13 15:18:21.309 INFO xxx.WebLogAspect : 請求開始時間:2020-01-13T15:18:21.309
2020-01-13 15:18:21.309 INFO xxx.WebLogAspect : 請求Url : http://localhost:8020/api/v1/hourlyStat/findHourlyStatReportList
2020-01-13 15:18:21.320 INFO xxx.WebLogAspect : 請求方式 : POST
2020-01-13 15:18:21.321 INFO xxx.WebLogAspect : 請求ip : 0:0:0:0:0:0:0:1
2020-01-13 15:18:21.322 INFO xxx.WebLogAspect : 請求方法 :
2020-01-13 15:18:21.325 INFO xxx.WebLogAspect : 請求參數(shù) : [HourlyStat{subStrategyId=null}]
2020-01-13 15:18:21.326 INFO xxx.WebLogAspect : 瀏覽器:CHROME
2020-01-13 15:18:21.327 INFO xxx.WebLogAspect : 瀏覽器版本:78.0.3904.108
2020-01-13 15:18:21.333 INFO xxx.WebLogAspect : 操作系統(tǒng): WINDOWS_10
2020-01-13 15:18:21.403 INFO xxx.WebLogAspect : 請求結(jié)束時間:2020-01-13T15:18:21.403
2020-01-13 15:18:21.405 INFO xxx.WebLogAspect : 請求耗時:94
2020-01-13 15:18:21.405 INFO xxx.WebLogAspect : 請求返回 : com.soyoung.ad.engine.common.Result@2f935d03
2020-01-13 15:18:21.492 INFO xxx.WebLogAspect : 請求結(jié)束時間:2020-01-13T15:18:21.492
2020-01-13 15:18:21.493 INFO xxx.WebLogAspect : 請求耗時:183
2020-01-13 15:18:21.494 INFO xxx.WebLogAspect : 請求返回 : com.soyoung.ad.engine.common.Result@7199e922
2020-01-13 15:18:42.859 INFO xxx.WebLogAspect : 請求開始時間:2020-01-13T15:18:42.859
2020-01-13 15:18:42.860 INFO xxx.WebLogAspect : 請求Url : http://localhost:8020/api/v1/hourlyStat/findHourlyStatReportList
2020-01-13 15:18:42.860 INFO xxx.WebLogAspect : 請求方式 : POST
2020-01-13 15:18:42.861 INFO xxx.WebLogAspect : 請求ip : 0:0:0:0:0:0:0:1
2020-01-13 15:18:42.861 INFO xxx.WebLogAspect : 請求方法 :
2020-01-13 15:18:42.862 INFO xxx.WebLogAspect : 請求參數(shù) : [HourlyStat{subStrategyId=1003}]
2020-01-13 15:18:42.862 INFO xxx.WebLogAspect : 瀏覽器:CHROME
2020-01-13 15:18:42.863 INFO xxx.WebLogAspect : 瀏覽器版本:78.0.3904.108
2020-01-13 15:18:42.864 INFO xxx.WebLogAspect : 操作系統(tǒng): WINDOWS_10
2020-01-13 15:18:42.909 INFO xxx.WebLogAspect : 請求結(jié)束時間:2020-01-13T15:18:42.909
2020-01-13 15:18:42.910 INFO xxx.WebLogAspect : 請求耗時:50
2020-01-13 15:18:42.910 INFO xxx.WebLogAspect : 請求返回 : com.soyoung.ad.engine.common.Result@7d494cbd
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- SpringBoot中使用AOP打印接口日志的方法
- SpringBoot中創(chuàng)建的AOP不生效的原因及解決
- springboot?aop里的@Pointcut()的配置方式
- SpringBoot AOP方式實(shí)現(xiàn)多數(shù)據(jù)源切換的方法
- SpringBoot項(xiàng)目中使用AOP的方法
- 詳解基于SpringBoot使用AOP技術(shù)實(shí)現(xiàn)操作日志管理
- SpringBoot使用AOP實(shí)現(xiàn)統(tǒng)計(jì)全局接口訪問次數(shù)詳解
- SpringBoot中使用AOP實(shí)現(xiàn)日志記錄功能
- SpringBoot使用AOP+注解實(shí)現(xiàn)簡單的權(quán)限驗(yàn)證的方法
- SpringBoot中AOP的多種用途與實(shí)踐指南
相關(guān)文章
springboot中shiro使用自定義注解屏蔽接口鑒權(quán)實(shí)現(xiàn)
本文主要介紹了springboot中shiro使用自定義注解屏蔽接口鑒權(quán)實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
Spring?Boot集成validation實(shí)現(xiàn)參數(shù)校驗(yàn)功能
Bean?Validation?是一個運(yùn)行時的數(shù)據(jù)驗(yàn)證框架,在驗(yàn)證之后驗(yàn)證的錯誤信息會被馬上返回,這篇文章主要介紹了Spring?Boot集成validation實(shí)現(xiàn)參數(shù)校驗(yàn)功能,需要的朋友可以參考下2024-05-05
MyBatis中RowBounds實(shí)現(xiàn)內(nèi)存分頁
RowBounds是MyBatis提供的一種內(nèi)存分頁方式,適用于小數(shù)據(jù)量的分頁場景,本文就來詳細(xì)的介紹一下,具有一定的參考價值,感興趣的可以了解一下2024-12-12
spring?boot獲取session的值為null問題及解決方法
我在登陸的時候,登陸成功后將name存進(jìn)了session,然后在獲取個人信息時取出session里的name的值為null,接下來通過本文給大家分享springboot獲取session的值為null問題,需要的朋友可以參考下2023-05-05
解決Spring session(redis存儲方式)監(jiān)聽導(dǎo)致創(chuàng)建大量redisMessageListenerConta
這篇文章主要介紹了解決Spring session(redis存儲方式)監(jiān)聽導(dǎo)致創(chuàng)建大量redisMessageListenerContailner-X線程問題,需要的朋友可以參考下2018-08-08
jmeter+ant+jenkins自動化測試環(huán)境配置搭建過程
在搭建jmeter+ant+jenkins環(huán)境有些前提條件,那就是要先配置好java環(huán)境、安裝好jenkins以及配置好jmeter,這樣才能省去很多的事情,對jmeter+ant+jenkins自動化測試環(huán)境配置搭建過程感興趣的朋友一起看看吧2021-12-12
json序列化時忽略值為null的字段2種方式實(shí)例
這篇文章主要給大家介紹了關(guān)于json序列化時忽略值為null的字段的2種方式,當(dāng)對象中某個字段為null時,我們希望將對象轉(zhuǎn)換成json時為null的字段不會被轉(zhuǎn)換到j(luò)son字符串,里面需要的朋友可以參考下2023-10-10
詳解springboot 使用c3p0數(shù)據(jù)庫連接池的方法
本篇文章主要介紹了springboot 使用c3p0數(shù)據(jù)庫連接池的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-09-09

