這篇文章將為大家詳細講解有關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() + "】方法發生異?!?quot; + 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() + " 方法發生異?!?quot; + 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() + "方法發生異?!?quot; + 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() + " 方法發生異?!?quot; + 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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。