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ào)用、對類成員的訪問等。
Pointcut(切入點):表示一個組Joint point,如方法名、參數(shù)類型、返回類型等等。
Advice(通知):Advice定義了在Pointcut里面定義的程序點具體要做的操作,它通過(before、around、after(return、throw)、finally來區(qū)別實在每個Joint point之前、之后還是執(zhí)行 前后要調(diào)用的代碼。
Before:在執(zhí)行方法前調(diào)用Advice,比如請求接口之前的登錄驗證。
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:實現(xiàn)上述切面編程的代碼織入,可以在編譯時刻,也可以在運行時刻,Spring和其它大多數(shù)Java框架都是在運行時刻生成代理。
二、代碼示例
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é)束時間戳(計時) */ private Long endTime; public WebLogAspect() { } /** * 定義請求日志切入點,其切入點表達(dá)式有多種匹配方式,這里是指定路徑 */ @Pointcut("execution(public * com.soyoung.ad.engine.controller.*.*(..))") public void webLogPointcut() { } /** * 前置通知: * 1. 在執(zhí)行目標(biāo)方法之前執(zhí)行,比如請求接口之前的登錄驗證; * 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. 在返回通知中補充請求日志信息,如返回時間,方法耗時,返回值,并且保存日志信息 * * @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ā)生異常或者拋出異常時執(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í)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java基于JDBC實現(xiàn)事務(wù),銀行轉(zhuǎn)賬及貨物進(jìn)出庫功能示例
這篇文章主要介紹了Java基于JDBC實現(xiàn)事務(wù),銀行轉(zhuǎn)賬及貨物進(jìn)出庫功能,較為詳細(xì)的分析了事務(wù)操作的原理、實現(xiàn)方法及java基于jdbc連接數(shù)據(jù)庫實現(xiàn)銀行事務(wù)操作的相關(guān)技巧,需要的朋友可以參考下2017-12-12SpringBoot中@EnableAsync和@Async注解的使用小結(jié)
在SpringBoot中,可以通過@EnableAsync注解來啟動異步方法調(diào)用的支持,通過@Async注解來標(biāo)識異步方法,讓方法能夠在異步線程中執(zhí)行,本文就來介紹一下,感興趣的可以了解一下2023-11-11Java使用阿里云接口進(jìn)行身份證實名認(rèn)證的示例實現(xiàn)
這篇文章主要介紹了使用阿里云接口進(jìn)行身份證實名認(rèn)證的示例實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07Spring Security使用Lambda DSL配置流程詳解
Spring Security 5.2 對 Lambda DSL 語法的增強(qiáng),允許使用lambda配置HttpSecurity、ServerHttpSecurity,重要提醒,之前的配置方法仍然有效。lambda的添加旨在提供更大的靈活性,但是用法是可選的。讓我們看一下HttpSecurity的lambda配置與以前的配置樣式相比2023-02-02詳解poi+springmvc+springjdbc導(dǎo)入導(dǎo)出excel實例
本篇文章主要介紹了poi+springmvc+springjdbc導(dǎo)入導(dǎo)出excel實例,非常具有實用價值,需要的朋友可以參考下。2017-01-01JVM內(nèi)存溢出和內(nèi)存泄漏的區(qū)別及說明
這篇文章主要介紹了JVM內(nèi)存溢出和內(nèi)存泄漏的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-02-02