1. 程式人生 > >46. Spring Boot中使用AOP統一處理Web請求日誌【從零開始學Spring Boot】

46. Spring Boot中使用AOP統一處理Web請求日誌【從零開始學Spring Boot】

【視訊&交流平臺】

http://study.163.com/course/introduction.htm?courseId=1004329008&utm_campaign=commission&utm_source=400000000155061&utm_medium=share

http://study.163.com/course/introduction.htm?courseId=1004638001&utm_campaign=commission&utm_source=400000000155061&utm_medium=share

https://gitee.com/happyangellxq520/spring-boot

http://412887952-qq-com.iteye.com/blog/2321532



在之前一系列的文章中都是提供了全部的程式碼,在之後的文章中就提供核心的程式碼進行講解。有什麼問題大家可以給我留言或者加我QQ,進行諮詢。

       AOP為Aspect Oriented Programming的縮寫,意為:面向切面程式設計,通過預編譯方式和執行期動態代理實現程式功能的統一維護的一種技術。AOP是Spring框架中的一個重要內容,它通過對既有程式定義一個切入點,然後在其前後切入不同的執行內容,比如常見的有:開啟資料庫連線/關閉資料庫連線、開啟事務/關閉事務、記錄日誌等。基於AOP不會破壞原來程式邏輯,因此它可以很好的對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。

本文就是要通過AOP技術統一處理web請求的日誌。

準備工作

因為需要對web請求做切面來記錄日誌,所以先引入web模組,並建立一個簡單的hello請求的處理。

<dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-web</artifactId>

</dependency>

實現一個簡單請求處理:

@RestController

publicclass HelloController {

       @RequestMapping("/hello")

       public Stringhello(Stringname,intstate){

              return"name"+name+"---"+state;

       }

}

這時候我們編寫一個啟動類啟動執行程式訪問:

引入AOP依賴

<dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-aop</artifactId>

</dependency>

在完成了引入AOP依賴包後,一般來說並不需要去做其他配置。也許在Spring中使用過註解配置方式的人會問是否需要在程式主類中增加@EnableAspectJAutoProxy來啟用,實際並不需要。

可以看下面關於AOP的預設配置屬性,其中spring.aop.auto屬性預設是開啟的,也就是說只要引入了AOP依賴後,預設已經增加了@EnableAspectJAutoProxy

# AOPspring.aop.auto=true # Add @EnableAspectJAutoProxy.

spring.aop.proxy-target-class=false# Whether subclass-based (CGLIB) proxies are to be created (true) as opposed tostandard Java interface-based proxies (false).

我在做測試的時候,以上兩個配置我都沒有進行使用,請自行進行測試。

而當我們需要使用CGLIB來實現AOP的時候,需要配置spring.aop.proxy-target-class=true,不然預設使用的是標準Java的實現。

實現Web層的日誌切面

實現AOP的切面主要有以下幾個要素:

·        使用@Aspect註解將一個java類定義為切面類

·        使用@Pointcut定義一個切入點,可以是一個規則表示式,比如下例中某個package下的所有函式,也可以是一個註解等。

·        根據需要在切入點不同位置的切入內容

o   使用@Before在切入點開始處切入內容

o   使用@After在切入點結尾處切入內容

o   使用@AfterReturning在切入點return內容之後切入內容(可以用來對處理返回值做一些加工處理)

o   使用@Around在切入點前後切入內容,並自己控制何時執行切入點自身的內容

o   使用@AfterThrowing用來處理當切入內容部分丟擲異常之後的處理邏輯。

o   

package com.kfit.config.aop.log;

import java.util.Arrays;

import java.util.Enumeration;

importjavax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

importorg.aspectj.lang.annotation.Pointcut;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

importorg.springframework.core.annotation.Order;

importorg.springframework.stereotype.Component;

importorg.springframework.web.context.request.RequestContextHolder;

importorg.springframework.web.context.request.ServletRequestAttributes;

/**

 *實現Web層的日誌切面

 *@author Angel(QQ:412887952)

 *@version v.0.1

 */

@Aspect

@Component

@Order(-5)

publicclass WebLogAspect {

       private Loggerlogger =  LoggerFactory.getLogger(this.getClass());

       /**

        *定義一個切入點.

        *解釋下:

        *

        * ~第一個 *代表任意修飾符及任意返回值.

        * ~第二個 *任意包名

        * ~第三個 *代表任意方法.

        * ~第四個 *定義在web包或者子包

        * ~第五個 *任意方法

        * ~ ..匹配任意數量的引數.

        */

        @Pointcut("execution(public * com.kfit.*.web..*.*(..))")

        publicvoid webLog(){}

        @Before("webLog()")

        publicvoid doBefore(JoinPointjoinPoint){

              // 接收到請求,記錄請求內容

               logger.info("WebLogAspect.doBefore()");

               ServletRequestAttributesattributes =(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

            HttpServletRequestrequest =attributes.getRequest();

         // 記錄下請求內容

        logger.info("URL : " + request.getRequestURL().toString());

        logger.info("HTTP_METHOD : " + request.getMethod());

        logger.info("IP : " + request.getRemoteAddr());

        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName()+ "." + joinPoint.getSignature().getName());

        logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));

      //獲取所有引數方法一:

        Enumeration<String> enu=request.getParameterNames(); 

        while(enu.hasMoreElements()){ 

               StringparaName=(String)enu.nextElement(); 

               System.out.println(paraName+": "+request.getParameter(paraName)); 

        } 

        }

        @AfterReturning("webLog()")

        publicvoid doAfterReturning(JoinPointjoinPoint){

              // 處理完請求,返回內容

               logger.info("WebLogAspect.doAfterReturning()");

        }

}

整個程式碼比較不好理解地方就是切點表示式,已經在註釋中詳細說明了,這裡不再過多的介紹。編碼中需要根據您自己的包命名規範進行修改下。

: WebLogAspect.doBefore()

: URL : http://127.0.0.1:8080/hello

: HTTP_METHOD : GET

: IP : 127.0.0.1

: CLASS_METHOD : com.kfit.demo.web.HelloController.hello

: ARGS : [林峰, 1]

name: 林峰

state: 1

: WebLogAspect.doAfterReturning()

: 耗時(毫秒) : 1

優化:AOP切面中的同步問題

在WebLogAspect切面中,分別通過doBefore和doAfterReturning兩個獨立函式實現了切點頭部和切點返回後執行的內容,若我們想統計請求的處理時間,就需要在doBefore處記錄時間,並在doAfterReturning處通過當前時間與開始處記錄的時間計算得到請求處理的消耗時間。

那麼我們是否可以在WebLogAspect切面中定義一個成員變數來給doBefore和doAfterReturning一起訪問呢?是否會有同步問題呢?

的確,直接在這裡定義基本型別會有同步問題,所以我們可以引入ThreadLocal物件,像下面這樣進行記錄改造程式碼為如下:

package com.kfit.config.aop.log;

import java.util.Arrays;

import java.util.Enumeration;

importjavax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;

importorg.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

importorg.aspectj.lang.annotation.Pointcut;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

importorg.springframework.stereotype.Component;

import org.springframework.web.context.request.RequestContextHolder;

importorg.springframework.web.context.request.ServletRequestAttributes;

/**

 *實現Web層的日誌切面

 *@author Angel(QQ:412887952)

 *@version v.0.1

 */

@Aspect

@Component

publicclass WebLogAspect {

       private Loggerlogger =  LoggerFactory.getLogger(this.getClass());

       ThreadLocal<Long>startTime =newThreadLocal<Long>();

       /**

        *定義一個切入點.

        *解釋下:

        *

        * ~第一個 *代表任意修飾符及任意返回值.

        * ~第二個 *任意包名

        * ~第三個 *代表任意方法.

        * ~第四個 *定義在web包或者子包

        * ~第五個 *任意方法

        * ~ ..匹配任意數量的引數.

        */

        @Pointcut("execution(public * com.kfit.*.web..*.*(..))")

        publicvoid webLog(){}

        @Before("webLog()")

        publicvoid doBefore(JoinPointjoinPoint){

               startTime.set(System.currentTimeMillis());

              // 接收到請求,記錄請求內容

               logger.info("WebLogAspect.doBefore()");

               ServletRequestAttributesattributes =(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

            HttpServletRequestrequest =attributes.getRequest();

         // 記錄下請求內容

        logger.info("URL : " + request.getRequestURL().toString());

        logger.info("HTTP_METHOD : " + request.getMethod());

        logger.info("IP : " + request.getRemoteAddr());

        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName()+ "." + joinPoint.getSignature().getName());

        logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));

      //獲取所有引數方法一:

        Enumeration<String> enu=request.getParameterNames(); 

        while(enu.hasMoreElements()){ 

               StringparaName=(String)enu.nextElement(); 

               System.out.println(paraName+": "+request.getParameter(paraName)); 

        } 

        }

        @AfterReturning("webLog()")

        publicvoid doAfterReturning(JoinPointjoinPoint){

              // 處理完請求,返回內容

               logger.info("WebLogAspect.doAfterReturning()");

               logger.info("耗時(毫秒) : " + (System.currentTimeMillis()-startTime.get()));

        }

}

優化:AOP切面的優先順序

由於通過AOP實現,程式得到了很好的解耦,但是也會帶來一些問題,比如:我們可能會對Web層做多個切面,校驗使用者,校驗頭資訊等等,這個時候經常會碰到切面的處理順序問題。

所以,我們需要定義每個切面的優先順序,我們需要@Order(i)註解來標識切面的優先順序。i的值越小,優先順序越高。假設我們還有一個切面是CheckNameAspect用來校驗name必須為didi,我們為其設定@Order(10),而上文中WebLogAspect設定為@Order(5),所以WebLogAspect有更高的優先順序,這個時候執行順序是這樣的:

·        @Before中優先執行@Order(5)的內容,再執行@Order(10)的內容

·        @After@AfterReturning中優先執行@Order(10)的內容,再執行@Order(5)的內容

所以我們可以這樣子總結:

>在切入點前的操作,按order的值由小到大執行

>在切入點後的操作,按order的值由大到小執行

在實際中order值可以設定為負值,確保是第一個進行執行的。

Spring Boot 系列視訊】

視訊&交流平臺:

http://study.163.com/course/introduction.htm?courseId=1004329008

http://412887952-qq-com.iteye.com/blog/2321532

網易雲課堂視訊最新更新

第十一章 Spring Boot 日誌

1、spring boot日誌—理論

2、Spring Boot日誌-logback

3、Spring Boot日誌-log4j2

第十二章 Spring Boot 知識點2

1、spring boot 服務配置和部署

2、Spring Boot 定製URL匹配規則

歷史章節

第一章 快速開始

1、Spring Boot之Hello World

2、Spring Boot之Hello World訪問404

第二章 Spring Boot之JSON

1、spring boot返回json資料

2、Spring Boot完美使用FastJson解析JSON資料

第三章 Spring Boot熱部署

1、Spring Boot熱部署(springloader)

2、springboot + devtools(熱部署)

第四章 Spring Boot資料庫

1、Spring Boot JPA/Hibernate/Spring Data概念

2、Spring Boot JPA-Hibernate

3、Spring Boot Spring Data JPA介紹

4、Spring Boot JdbcTemplate

5、Spring Boot整合MyBatis

第五章 web開發

1、全域性異常捕捉

2、配置server資訊

3、spring boot使用thymeleaf

4、Spring Boot 使用freemarker

5、Spring Boot新增JSP支援

第六章 定時任務

1、Spring Boot定時任務

2、Spring Boot 定時任務升級篇(動態修改cron引數)

3、Spring Boot 定時任務升級篇(動態新增修改刪除定時任務)

4、Spring Boot 定時任務升級篇(叢集/分散式下的定時任務說明)

5、Spring Boot Quartz介紹

6、Spring Boot Quartz在Java Project中使用

7、Spring Boot 整合Quartz普通使用

8、Spring Boot 整合Quartz升級版

9、Spring Boot 整合Quartz二次升級版

10、Spring Boot 整合Quartz-Job如何自動注入Spring容器託管的物件

第七章 Spring Boot MyBatis升級篇

1、Spring Boot MyBatis升級篇-註解

2、Spring Boot MyBatis升級篇-註解-自增ID

3、Spring Boot MyBatis升級篇-註解-增刪改查

4、Spring Boot MyBatis升級篇-註解-分頁查詢

5、Spring Boot MyBatis升級篇-註解-分頁PageHelper不生效

6、Spring Boot MyBatis升級篇-註解- mybatic insert異常:BindingException: Parameter 'name' not found

7、Spring Boot MyBatis升級篇-註解- #和$符號特別篇

8、Spring Boot MyBatis升級篇-註解[email protected]

9、Spring Boot MyBatis升級篇-註解-動態SQL(if test)-方案一:<script>

10、Spring Boot MyBatis升級篇-註解-動態SQL(if test)-方案二:@Provider

11、Spring Boot MyBatis升級篇-註解-動態SQL-引數問題

12、Spring Boot MyBatis升級篇-註解-特別篇:@MapperScan和@Mapper

13、Spring Boot MyBatis升級篇-XML

14、Spring Boot MyBatis升級篇-XML-自增ID

15、Spring Boot MyBatis升級篇-XML-增刪改查

16、Spring Boot MyBatis升級篇-XML-分頁查詢

17、Spring Boot MyBatis升級篇-XML-分頁PageHelper不生效

18、Spring Boot MyBatis升級篇-XML-動態SQL(if test)

19、Spring Boot MyBatis升級篇-XML-註解-初嘗試

20、Spring Boot MyBatis升級篇- pagehelper替換為pagehelper-spring-boot-starter

第八章 Spring Boot 知識點1

1、Spring Boot 攔截器HandlerInterceptor

2、Spring Boot啟動載入資料CommandLineRunner

3、Spring Boot環境變數讀取和屬性物件的繫結

4、Spring Boot使用自定義的properties

5、Spring Boot使用自定義的properties

6、Spring Boot使用@SpringBootApplication

7、Spring Boot 監控和管理生產環境

第十章 Spring Boot 打包部署

1、Spring Boot打包部署((提供Linux的sh檔案))

第十一章 Spring Boot 日誌

1、spring boot日誌—理論

2、Spring Boot日誌-logback

3、Spring Boot日誌-log4j2


相關推薦

46. Spring Boot使用AOP統一處理Web請求日誌開始Spring Boot

【視訊&交流平臺】 http://study.163.com/course/introduction.htm?courseId=1004329008&utm_campaign=commission&utm_source=40000000

Spring Boot使用AOP統一處理Web請求日誌

在spring boot中,簡單幾步,使用spring AOP實現一個攔截器: 1、引入依賴: [html] view plain copy print?<dependency>    <groupId>org.springframework.boot</groupId&g

Spring Boot2.0之統一處理web請求日誌

試問,你的專案中,如果有幾萬個方法,你還這麼寫log.info("name"+name+",age"+age )日誌麼?low~      所以用AOP呀 1、首先建立個aop的包(aop的依賴jar包要在pom中搞定),把下面類,貼進去:   packa

SpringBoot使用AOP統一處理Web請求日誌新增MDC

AOP是Spring框架中的一個重要內容,它通過對既有程式定義一個切入點,然後在其前後切入不同的執行內容,基於AOP不會破壞原來程式邏輯,因此它可以很好的對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。之前看到公

用SpringBoot搭建個人部落格01-----使用AOP統一處理Web請求日誌

摘要 AOP 是面向切面的程式設計,就是在執行期通過動態代理的方式對程式碼進行增強處理,比較核心的概念有 切點,切面,通知,有關AOP的詳情參考:。 本文要介紹的是在一個SpringBoot專案中如何統一的處理Web請求日誌,基本思想還是採用AOP的方式,攔截請

41. Spring Boot 使用Java程式碼建立Bean並註冊到Spring開始Spring Boot

【視訊&交流平臺】 http://study.163.com/course/introduction.htm?courseId=1004329008&utm_campaign=commission&utm_source=40000000

Spring Boot使用模板freemarker開始Spring Boot(轉)

dep demo attach macro 使用 doctype com 地址 2016年 視頻&交流平臺:à SpringBoot網易雲課堂視頻http://study.163.com/course/introduction.htm?courseId=10

資料新增非同步解析重新整理大資料量redis (——)(三)Spring Boot普通類呼叫bean開始Spring Boot

部落格分類:  從零開始學Spring Boot 從零開始學Spring BootSpring Boot普通類呼叫bean    【視訊&交流平臺】 à SpringBoot視訊 http://stu

33. Spring Boot 監控和管理生產環境開始Spring Boot

【視訊&交流平臺】 http://study.163.com/course/introduction.htm?courseId=1004329008&utm_campaign=commission&utm_source=40000000

(33)Spring Boot 監控和管理生產環境開始Spring Boot

spring-boot-actuator模組提供了一個監控和管理生產環境的模組,可以使用http、jmx、ssh、telnet等拉管理和監控應用。審計(Auditing)、 健康(health)、資料採集(metrics gathering)會自動加入到應用裡面。 首先,寫

8. 使用JPA儲存資料開始Spring Boot

【視訊&交流平臺】 http://study.163.com/course/introduction.htm?courseId=1004329008&utm_campaign=commission&utm_source=40000000

103. Spring Boot Freemarker特別篇之contextPath開始Spring Boot

需求緣起:有人在群裡@我:請教群主大神一個問題,spring boot  + freemarker 怎麼獲取contextPath 頭疼死我了,網上沒一個靠譜的 。我就看看之前部落格中的 【Spring Boot使用模板freemarker】好像確實沒有介紹到在.ftl檔案

31. Spring Boot匯入XML配置開始Spring Boot

【視訊&交流平臺】 http://study.163.com/course/introduction.htm?courseId=1004329008&utm_campaign=commission&utm_source=40000000

2. Spring Boot返回json資料開始Spring Boot

【視訊&交流平臺】 http://study.163.com/course/introduction.htm?courseId=1004329008&utm_campaign=commission&utm_source=40000000

35. Spring Boot整合Redis實現快取機制開始Spring Boot

【視訊&交流平臺】 http://study.163.com/course/introduction.htm?courseId=1004329008&utm_campaign=commission&utm_source=40000000

51. spring boot屬性檔案之多環境配置開始Spring Boot

【視訊&交流平臺】 http://study.163.com/course/introduction.htm?courseId=1004329008&utm_campaign=commission&utm_source=40000000

(27)Spring Boot Junit單元測試開始Spring Boot

Junit這種老技術,現在又拿出來說,不為別的,某種程度上來說,更是為了要說明它在專案中的重要性。 那麼先簡單說一下為什麼要寫測試用例 1. 可以避免測試點的遺漏,為了更好的進行測試,可以提高測試效率 2. 可以自動測試,可以在專案打包前進行測試校驗 3. 可以及時發現因為

40. springboot + devtools(熱部署)開始Spring Boot

【視訊&交流平臺】 http://study.163.com/course/introduction.htm?courseId=1004329008&utm_campaign=commission&utm_source=40000000

104. Spring Boot 啟動流程分析第三篇開始Spring Boot

問題的提出:Spring Boot不像spring需要定義xml檔案檔案,那麼spring boot是如何在沒有配置檔案的情況下為我們啟動一個完整的WEB工程的呢。我們先@SpringBootApplication開始分析下這個啟動流程,原始碼取自:1.4.1.RELEAS

(4)Spring Boot完美使用FastJson解析JSON資料開始Spring Boot

個人使用比較習慣的json框架是fastjson,所以spring boot預設的json使用起來就很陌生了,所以很自然我就想我能不能使用fastjson進行json解析呢? 引入fastjson依賴庫:   <dependencies>         <