今天就跟大家聊聊有關(guān)怎么在Spring Boot中配置AOP打印日志,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
漣水網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)公司!從網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營維護(hù)。創(chuàng)新互聯(lián)公司于2013年成立到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)公司。
一、Spring AOP
AOP(Aspect-Oriented Programming,面向切面編程),它利用一種"橫切"的技術(shù),將那些多個(gè)類的共同行為封裝到一個(gè)可重用的模塊。便于減少系統(tǒng)的重復(fù)代碼,降低模塊之間的耦合度,并有利于未來的可操作性和可維護(hù)性。
AOP中有以下概念:
Aspect(切面):聲明類似于Java中的類聲明,在Aspect中會包含一些Pointcut及相應(yīng)的Advice。
Joint point(連接點(diǎn)):表示在程序中明確定義的點(diǎn)。包括方法的調(diào)用、對類成員的訪問等。
Pointcut(切入點(diǎn)):表示一個(gè)組Joint point,如方法名、參數(shù)類型、返回類型等等。
Advice(通知):Advice定義了在Pointcut里面定義的程序點(diǎn)具體要做的操作,它通過(before、around、after(return、throw)、finally來區(qū)別實(shí)在每個(gè)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)上述切面編程的代碼織入,可以在編譯時(shí)刻,也可以在運(yùn)行時(shí)刻,Spring和其它大多數(shù)Java框架都是在運(yùn)行時(shí)刻生成代理。
二、項(xiàng)目示例
當(dāng)然,在使用該案例之前,如果需要了解日志配置相關(guān),可參考 SpringBoot 異步輸出 Logback 日志, 本文就不再概述了。
2.1 在pom引入依賴
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-aop eu.bitwalker UserAgentUtils 1.20 org.projectlombok lombok 1.8.4
2.2 Controller 切面:WebLogAspect
@Aspect @Component @Slf4j public class WebLogAspect { /** * 進(jìn)入方法時(shí)間戳 */ private Long startTime; /** * 方法結(jié)束時(shí)間戳(計(jì)時(shí)) */ private Long endTime; public WebLogAspect() { } /** * 定義請求日志切入點(diǎn),其切入點(diǎn)表達(dá)式有多種匹配方式,這里是指定路徑 */ @Pointcut("execution(public * cn.van.log.aop.controller.*.*(..))") public void webLogPointcut() { } /** * 前置通知: * 1. 在執(zhí)行目標(biāo)方法之前執(zhí)行,比如請求接口之前的登錄驗(yàn)證; * 2. 在前置通知中設(shè)置請求日志信息,如開始時(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("請求開始時(shí)間:{}" + 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ǔ)充請求日志信息,如返回時(shí)間,方法耗時(shí),返回值,并且保存日志信息 * * @param ret * @throws Throwable */ @AfterReturning(returning = "ret", pointcut = "webLogPointcut()") public void doAfterReturning(Object ret) throws Throwable { endTime = System.currentTimeMillis(); log.info("請求結(jié)束時(shí)間:{}" + LocalDateTime.now()); log.info("請求耗時(shí):{}" + (endTime - startTime)); // 處理完請求,返回內(nèi)容 log.info("請求返回 : {}" + ret); } /** * 異常通知: * 1. 在目標(biāo)方法非正常結(jié)束,發(fā)生異?;蛘邟伋霎惓r(shí)執(zhí)行 * 1. 在異常通知中設(shè)置異常信息,并將其保存 * * @param throwable */ @AfterThrowing(value = "webLogPointcut()", throwing = "throwable") public void doAfterThrowing(Throwable throwable) { // 保存異常日志記錄 log.error("發(fā)生異常時(shí)間:{}" + LocalDateTime.now()); log.error("拋出異常:{}" + throwable.getMessage()); } }
2.3 編寫測試
@RestController @RequestMapping("/log") public class LogbackController { /** * 測試正常請求 * @param msg * @return */ @GetMapping("/{msg}") public String getMsg(@PathVariable String msg) { return "request msg : " + msg; } /** * 測試拋異常 * @return */ @GetMapping("/test") public String getException(){ // 故意造出一個(gè)異常 Integer.parseInt("abc123"); return "success"; } }
2.4 @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é)束時(shí)間:"+ LocalDateTime.now()); log.info("請求耗時(shí):{}" , (System.currentTimeMillis() - startTime)); // 處理完請求,返回內(nèi)容 log.info("請求返回 : " , ret); return ret; }
三、 測試
3.1 請求入口LogbackController.java
@RestController @RequestMapping("/log") public class LogbackController { /** * 測試正常請求 * @param msg * @return */ @GetMapping("/normal/{msg}") public String getMsg(@PathVariable String msg) { return msg; } /** * 測試拋異常 * @return */ @GetMapping("/exception/{msg}") public String getException(@PathVariable String msg){ // 故意造出一個(gè)異常 Integer.parseInt("abc123"); return msg; } }
3.2 測試正常請求
打開瀏覽器,訪問http://localhost:8082/log/normal/hello
日志打印如下:
[2019-02-24 22:37:50.050] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-1] [65] [INFO ] 請求開始時(shí)間:2019-02-24T22:37:50.892
[2019-02-24 22:37:50.050] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-1] [66] [INFO ] 請求Url : http://localhost:8082/log/normal/hello
[2019-02-24 22:37:50.050] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-1] [67] [INFO ] 請求方式 : GET
[2019-02-24 22:37:50.050] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-1] [68] [INFO ] 請求ip : 0:0:0:0:0:0:0:1
[2019-02-24 22:37:50.050] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-1] [69] [INFO ] 請求方法 :
[2019-02-24 22:37:50.050] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-1] [70] [INFO ] 請求參數(shù) : [hello]
[2019-02-24 22:37:50.050] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-1] [72] [INFO ] 瀏覽器:CHROME
[2019-02-24 22:37:50.050] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-1] [73] [INFO ] 瀏覽器版本:76.0.3809.100
[2019-02-24 22:37:50.050] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-1] [74] [INFO ] 操作系統(tǒng): MAC_OS_X
[2019-02-24 22:37:50.050] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-1] [88] [INFO ] 請求結(jié)束時(shí)間:2019-02-24T22:37:50.901
[2019-02-24 22:37:50.050] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-1] [89] [INFO ] 請求耗時(shí):14
[2019-02-24 22:37:50.050] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-1] [91] [INFO ] 請求返回 : hello
3.3 測試異常情況
訪問:http://localhost:8082/log/exception/hello
[2019-02-24 22:39:57.057] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-9] [65] [INFO ] 請求開始時(shí)間:2019-02-24T22:39:57.728
[2019-02-24 22:39:57.057] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-9] [66] [INFO ] 請求Url : http://localhost:8082/log/exception/hello
[2019-02-24 22:39:57.057] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-9] [67] [INFO ] 請求方式 : GET
[2019-02-24 22:39:57.057] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-9] [68] [INFO ] 請求ip : 0:0:0:0:0:0:0:1
[2019-02-24 22:39:57.057] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-9] [69] [INFO ] 請求方法 :
[2019-02-24 22:39:57.057] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-9] [70] [INFO ] 請求參數(shù) : [hello]
[2019-02-24 22:39:57.057] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-9] [72] [INFO ] 瀏覽器:CHROME
[2019-02-24 22:39:57.057] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-9] [73] [INFO ] 瀏覽器版本:76.0.3809.100
[2019-02-24 22:39:57.057] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-9] [74] [INFO ] 操作系統(tǒng): MAC_OS_X
[2019-02-24 22:39:57.057] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-9] [104] [ERROR] 發(fā)生異常時(shí)間:2019-02-24T22:39:57.731
[2019-02-24 22:39:57.057] [cn.van.log.aop.aspect.WebLogAspect] [http-nio-8082-exec-9] [105] [ERROR] 拋出異常:For input string: "abc123"
[2019-02-24 22:39:57.057] [org.apache.juli.logging.DirectJDKLog] [http-nio-8082-exec-9] [175] [ERROR] Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NumberFormatException: For input string: "abc123"] with root cause
java.lang.NumberFormatException: For input string: "abc123"
springboot一種全新的編程規(guī)范,其設(shè)計(jì)目的是用來簡化新Spring應(yīng)用的初始搭建以及開發(fā)過程,SpringBoot也是一個(gè)服務(wù)于框架的框架,服務(wù)范圍是簡化配置文件。
看完上述內(nèi)容,你們對怎么在Spring Boot中配置AOP打印日志有進(jìn)一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。