您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Springboot2 如何去配置AOP日志,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
Spring boot2 配置AOP前置增強,后置增強,異常增強,環繞增強,最終增強
關于AOP切面相關具體概念不做過多闡述(概念弄懂有利于理解思想),這是配置AOP的各種增強日志,解決日志嵌套在業務代碼的麻煩和不科學
先來個Git demo項目壓壓驚: https://github.com/zhang-xiao-xiang/boot-aop (有的更新了一些)
1pom依賴(這里使用log4j2作為日志框架,因為比log4j或者其他日志框架,它效率更高,功能更加強大)
<!-- 引入log4j2依賴(注意還有排除依賴) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> <!-- 排除exclude掉spring-boot的默認log框架配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <!-- 加上這個才能辨認到log4j2.yml文件(如果日志要輸出到本地或者具體磁盤,需要配置yml) --> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-yaml</artifactId> </dependency> <!--AOP的支持依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
2編寫切面類
package com.example.nba.aop;//改成你的包名即可 import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; /** * AopLog * * @author 10905 2019/1/4 * @version 1.0 */ @Aspect @Component public class AopLog { //使用org.slf4j.Logger,這是Spring實現日志的方法 private final static Logger logger = LoggerFactory.getLogger(AopLog.class); // 切入點注解的表達式:就是需要AOP的地方(一般是業務邏輯層service,當然服務接口調用層controller也行,兩者一起打印日志也行 // 這個類似正則表達式,可以控制日志的精度(包下,類下,方法下)和切面的類型(業務層面,服務接口層面)相當靈活) @Pointcut("execution(* com.example.nba.controller.PlayerApi.*(..))") // @Pointcut("execution(* com.example.nba.repository.PlayerRep.*(..))") //切入點簽名的方法,注意返回值必須是void,相當于切入點的無參構造 public void mypointcut() { } // 前置增強 @Before("mypointcut()") public void Mybefore(JoinPoint jp) { logger.info("*前置增強*調用了【" + jp.getTarget().getClass().getSimpleName() + "】的【" + jp.getSignature().getName() + "】的方法,方法入參為【" + Arrays.toString(jp.getArgs()) + "】"); // 接收到請求,記錄請求內容(這里同樣可以在前置增強配置請求的相關信息) ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); logger.info("請求的地址URL : " + request.getRequestURL().toString()); logger.info("請求的方式HTTP_METHOD : " + request.getMethod()); logger.info("請求的IP : " + request.getRemoteAddr()); logger.info("請求的全類名 : " + jp.getSignature().getDeclaringTypeName() + "." + jp.getSignature().getName()); logger.info("請求的參數(數組形式) : " + Arrays.toString(jp.getArgs())); } //后置增強 @AfterReturning(pointcut = "mypointcut()", returning = "result") public void MyafterReturing(JoinPoint jp, Object result) { logger.info("*后置增強*調用了【" + jp.getTarget().getClass().getSimpleName() + "】的【" + jp.getSignature().getName() + "】的方法,方法返回值【" + result + "】"); } // 異常拋出增強 @AfterThrowing(pointcut = "mypointcut()", throwing = "e") public void afterThrowing(JoinPoint jp, RuntimeException e) { logger.error("*異常增強*【" + jp.getSignature().getName().getClass().getSimpleName() + "】方法發生異常【" + e + "】"); } // 最終增強 @After("mypointcut()") public void afterLogger(JoinPoint jp) { logger.info("*最終增強*【" + jp.getSignature().getName() + "】方法結束執行。"); } //環繞增強 @Around("mypointcut()") public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable { logger.info("在==>>" + jp.getTarget().getClass().getName() + "類里面使用AOP環繞增強=="); logger.info("*環繞增強*調用【" + jp.getTarget().getClass().getSimpleName() + "】的【 " + jp.getSignature().getName() + "】方法。方法入參【" + Arrays.toString(jp.getArgs()) + "】"); try { Object result = jp.proceed(); logger.info("*環繞增強*調用 " + jp.getTarget() + "的【 " + jp.getSignature().getName() + "】方法。方法返回值【" + result + "】"); return result; } catch (Throwable e) { logger.error(jp.getSignature().getName() + " 方法發生異常【" + e + "】"); throw e; } finally { logger.info("*環繞增強*執行finally【" + jp.getSignature().getName() + "】方法結束執行<<==。"); } } }
3測試(數據庫代碼和業務層等代碼就不貼上去了,主要參考AOP的pom依賴和切面類),比如我瀏覽器請求
http://localhost:8080/player/findAll
控制器顯示:
json格式的aop配置(注意文件格式入參需要判斷一下(因為excel,PDF,png等格式無法轉json)),假如現在的需求是請求和返回參數均以json格式為主,并且記錄日志到數據庫(排除查詢日志的接口,因為查詢日志不需要記錄到數據庫,這里涉及到AOP排除某個接口或者說是某個切面,具體見下)
日志實體(表)
package com.xinzuo.lvyou.pojo; import java.io.Serializable; import java.util.Date; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; /** * <p> * 日志記錄監控表 * </p> * * @author zhangxiaoxiang * @since 2019-07-08 */ @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) public class LogMonitor implements Serializable { private static final long serialVersionUID = 1L; /** * 日志主鍵ID */ @TableId private String logId; /** * 請求IP地址 */ private String requestIp; /** * 操作接口反饋描述 */ private String summarize; /** * 響應狀態嗎 */ private Integer responseCode; /** * 請求方式 */ private String requestType; /** * 請求接口 */ private String requestApi; /** * 請求參數 */ private String requestPara; /** * 返回參數 */ private String responsePara; /** * 請求到響應處理時間(毫秒) */ private Integer responseTime; /** * 創建時間(請求發出的時間) */ private Date createTime; /** * 日志所屬(0移動前端日志,1PC端日志) */ private Integer belongTo; }
下面是json格式并帶有記錄到數據庫的需求的,假如返回的格式是三段式json,如下
{ "code": 200, "msg": "查詢打卡次數成功", "data": { "photoSave": null, "user": null, "amount": 27 } }
切面類的編寫如下
package com.xinzuo.lvyou.aop; import com.alibaba.fastjson.JSONArray; import com.gexin.fastjson.JSON; import com.gexin.fastjson.JSONObject; import com.xinzuo.lvyou.dao.LogMonitorDao; import com.xinzuo.lvyou.pojo.LogMonitor; import com.xinzuo.lvyou.util.KeyUtil; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.Date; /** * AopLog:日志切面類 * * @author zhangxiaoxiang * @date: 2019/05/22 */ @Component @Aspect public class AopJsonLog { /** * 記錄日志到數據庫 */ @Autowired private LogMonitorDao logMonitorDao; //記錄到數據庫 LogMonitor logMonitor = new LogMonitor(); long start = 0; /** * 使用org.slf4j.Logger,這是Spring實現日志的方法 */ private final static Logger logger = LoggerFactory.getLogger(AopJsonLog.class); @Pointcut("execution(* com.xinzuo.lvyou.admin..*(..)) ") public void myPointcut() { } /** * 前置增強 單獨配置的前端接口切面 * 日志打印排除在外,查詢日志接口就不記錄數據庫 * 排除某個方法使用!execution(* com.xinzuo.lvyou.controller.LogMonitorController.*(..)) * * @param jp */ @Before("execution(* com.xinzuo.lvyou.controller..*(..)) && !execution(* com.xinzuo.lvyou.controller.LogMonitorController.*(..))") public void MyBefore(JoinPoint jp) { logger.info("---------------------------------前端請求接口日志----------------------------------------------"); // 接收到請求,記錄請求內容(這里同樣可以在前置增強配置請求的相關信息) ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); logger.info("訪問的接口:" + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName()); try { logger.info("請求入參為:" + new JSONArray(Arrays.asList(jp.getArgs())).toString()); } catch (Exception e) { logger.info("請求入參為:圖片,視頻,excel,PDF等格式(此時無法轉換成JSON格式)"); //由于知道這里異常的原因是json轉換參數異常,所以就不打印了,不捕獲,以免控制臺難看或者日志難看 //e.printStackTrace(); } //logger.info("請求的地址URL: " + request.getRequestURL().toString()); //logger.info("請求的方式HTTP_METHOD: " + request.getMethod()); //logger.info("請求的IP: " + request.getRemoteAddr()); start = System.currentTimeMillis(); logMonitor.setLogId(KeyUtil.genUniqueKey()); logMonitor.setRequestType(request.getMethod()); logMonitor.setRequestApi(jp.getTarget().getClass().getName() + "." + jp.getSignature().getName()); try { logMonitor.setRequestPara(new JSONArray(Arrays.asList(jp.getArgs())).toString()); } catch (Exception e) { logMonitor.setRequestPara("請求入參為:圖片,視頻,excel,PDF等格式(此時無法轉換成JSON格式)"); } logMonitor.setRequestIp(request.getRemoteAddr()); logMonitor.setCreateTime(new Date()); } /** * 后置增強 * 排除某個方法使用!execution(* com.xinzuo.lvyou.controller.LogMonitorController.*(..)) * 日志打印排除在外,查詢日志接口就不記錄數據庫 * @param jp * @param vo */ @AfterReturning(pointcut = "execution(* com.xinzuo.lvyou.controller..*(..)) && !execution(* com.xinzuo.lvyou.controller.LogMonitorController.*(..))", returning = "vo") public void MyafterReturing(JoinPoint jp, Object vo) { //logger.info("訪問的接口: " + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName()); //入參打印json數組格式 // try { // logger.info("請求入參為:" + new JSONArray(Arrays.asList(jp.getArgs())).toString()); // } catch (Exception e) { // logger.info("請求入參為圖片,視頻,excel,PDF等格式(此時無法轉換成JSON格式)"); // //由于知道這里異常的原因是json轉換參數異常,所以就不打印了,不捕獲,以免控制臺難看或者日志難看 // //e.printStackTrace(); // } logger.info("方法返回值:" + JSON.toJSONString(vo)); logMonitor.setResponsePara(JSON.toJSONString(vo)); long end = System.currentTimeMillis(); //解析json JSONObject json= null; try { json = JSON.parseObject(JSON.toJSONString(vo)); logMonitor.setResponseCode(Integer.valueOf(json.get("code").toString())); logMonitor.setSummarize(json.get("msg").toString()); } catch (Exception e) { logMonitor.setSummarize("進行圖片,視頻,excel,PDF等格式操作(由于這個操作特殊一點,所以后臺直接把返回狀態碼默認為200,以實際為準)"); logMonitor.setResponseCode(200); //e.printStackTrace(); } logMonitor.setBelongTo(0); logMonitor.setResponseTime((int) (end - start)); try { logMonitorDao.insert(logMonitor); } catch (Exception e) { logger.info("AOP系統故障!"); } } // /** // * 異常拋出增強 // * // * @param jp // * @param e // */ // @AfterThrowing(pointcut = "myPointcut()", throwing = "e") // public void afterThrowing(JoinPoint jp, RuntimeException e) { // logger.error("異常增強:" + jp.getSignature().getName().getClass().getSimpleName() + "方法發生異常【" + e + "】"); // } /** * 環繞增強 :測試的時候finally的切面日志注釋不打印,因為日志多了反而不好調試,上線時再取消注釋 * * @param jp * @return * @throws Throwable */ @Around("myPointcut()") public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable { logger.info("admin---------------------------------后臺請求接口日志----------------------------------------------"); logger.info("訪問的接口:" + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName()); //入參打印json數組格式 try { logger.info("請求入參為:" + new JSONArray(Arrays.asList(jp.getArgs())).toString()); } catch (Exception e) { logger.info("請求入參為:圖片,視頻,excel,PDF等格式(此時無法轉換成JSON格式)"); //由于知道這里異常的原因是json轉換參數異常,所以就不打印了,不捕獲,以免控制臺難看或者日志難看 //e.printStackTrace(); } try { Object result = jp.proceed(); logger.info("方法返回值:" + JSON.toJSONString(result)); return result; } catch (Throwable e) { logger.info("訪問的接口:" + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName()); logger.info("請求入參為:" + new JSONArray(Arrays.asList(jp.getArgs())).toString()); logger.error(jp.getSignature().getName() + " 方法發生異常【" + e + "】"); throw e; } finally { //logger.info("訪問的接口: " + jp.getTarget().getClass().getName() + "."+jp.getSignature().getName()); //logger.info("請求入參為: "+ new JSONArray(Arrays.asList(jp.getArgs())).toString()); //logger.info("執行 :" + jp.getSignature().getName() + "方法結束。"); } } }
------------------------------------下面是spring AOP配置需要的額外依賴-----------------------------------------
<dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.12</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.12</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2</version> </dependency>
關于Springboot2 如何去配置AOP日志就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。