Springboot2 配置AOP日志的方法步驟
Spring boot2 配置AOP前置增強(qiáng),后置增強(qiáng),異常增強(qiáng),環(huán)繞增強(qiáng),最終增強(qiáng)
關(guān)于AOP切面相關(guān)具體概念不做過多闡述(概念弄懂有利于理解思想),這是配置AOP的各種增強(qiáng)日志,解決日志嵌套在業(yè)務(wù)代碼的麻煩和不科學(xué)
先來個(gè)Git demo項(xiàng)目壓壓驚: https://github.com/zhang-xiao-xiang/boot-aop (有的更新了一些)
1pom依賴(這里使用log4j2作為日志框架,因?yàn)楸萳og4j或者其他日志框架,它效率更高,功能更加強(qiáng)大)
<!-- 引入log4j2依賴(注意還有排除依賴) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- 排除exclude掉spring-boot的默認(rèn)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>
<!-- 加上這個(gè)才能辨認(rèn)到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實(shí)現(xiàn)日志的方法
private final static Logger logger = LoggerFactory.getLogger(AopLog.class);
// 切入點(diǎn)注解的表達(dá)式:就是需要AOP的地方(一般是業(yè)務(wù)邏輯層service,當(dāng)然服務(wù)接口調(diào)用層controller也行,兩者一起打印日志也行
// 這個(gè)類似正則表達(dá)式,可以控制日志的精度(包下,類下,方法下)和切面的類型(業(yè)務(wù)層面,服務(wù)接口層面)相當(dāng)靈活)
@Pointcut("execution(* com.example.nba.controller.PlayerApi.*(..))")
// @Pointcut("execution(* com.example.nba.repository.PlayerRep.*(..))")
//切入點(diǎn)簽名的方法,注意返回值必須是void,相當(dāng)于切入點(diǎn)的無參構(gòu)造
public void mypointcut() {
}
// 前置增強(qiáng)
@Before("mypointcut()")
public void Mybefore(JoinPoint jp) {
logger.info("*前置增強(qiáng)*調(diào)用了【" + jp.getTarget().getClass().getSimpleName() +
"】的【" + jp.getSignature().getName() + "】的方法,方法入?yún)椤?
+ Arrays.toString(jp.getArgs()) + "】");
// 接收到請(qǐng)求,記錄請(qǐng)求內(nèi)容(這里同樣可以在前置增強(qiáng)配置請(qǐng)求的相關(guān)信息)
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
logger.info("請(qǐng)求的地址URL : " + request.getRequestURL().toString());
logger.info("請(qǐng)求的方式HTTP_METHOD : " + request.getMethod());
logger.info("請(qǐng)求的IP : " + request.getRemoteAddr());
logger.info("請(qǐng)求的全類名 : " + jp.getSignature().getDeclaringTypeName() + "." + jp.getSignature().getName());
logger.info("請(qǐng)求的參數(shù)(數(shù)組形式) : " + Arrays.toString(jp.getArgs()));
}
//后置增強(qiáng)
@AfterReturning(pointcut = "mypointcut()", returning = "result")
public void MyafterReturing(JoinPoint jp, Object result) {
logger.info("*后置增強(qiáng)*調(diào)用了【" + jp.getTarget().getClass().getSimpleName() +
"】的【" + jp.getSignature().getName() + "】的方法,方法返回值【" + result + "】");
}
// 異常拋出增強(qiáng)
@AfterThrowing(pointcut = "mypointcut()", throwing = "e")
public void afterThrowing(JoinPoint jp, RuntimeException e) {
logger.error("*異常增強(qiáng)*【" + jp.getSignature().getName().getClass().getSimpleName() + "】方法發(fā)生異常【" + e + "】");
}
// 最終增強(qiáng)
@After("mypointcut()")
public void afterLogger(JoinPoint jp) {
logger.info("*最終增強(qiáng)*【" + jp.getSignature().getName() + "】方法結(jié)束執(zhí)行。");
}
//環(huán)繞增強(qiáng)
@Around("mypointcut()")
public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable {
logger.info("在==>>" + jp.getTarget().getClass().getName() + "類里面使用AOP環(huán)繞增強(qiáng)==");
logger.info("*環(huán)繞增強(qiáng)*調(diào)用【" + jp.getTarget().getClass().getSimpleName() + "】的【 " + jp.getSignature().getName()
+ "】方法。方法入?yún)ⅰ? + Arrays.toString(jp.getArgs()) + "】");
try {
Object result = jp.proceed();
logger.info("*環(huán)繞增強(qiáng)*調(diào)用 " + jp.getTarget() + "的【 "
+ jp.getSignature().getName() + "】方法。方法返回值【" + result + "】");
return result;
} catch (Throwable e) {
logger.error(jp.getSignature().getName() + " 方法發(fā)生異常【" + e + "】");
throw e;
} finally {
logger.info("*環(huán)繞增強(qiáng)*執(zhí)行finally【" + jp.getSignature().getName() + "】方法結(jié)束執(zhí)行<<==。");
}
}
}
3測(cè)試(數(shù)據(jù)庫(kù)代碼和業(yè)務(wù)層等代碼就不貼上去了,主要參考AOP的pom依賴和切面類),比如我瀏覽器請(qǐng)求
http://localhost:8080/player/findAll
控制器顯示:

json格式的aop配置(注意文件格式入?yún)⑿枰袛嘁幌?因?yàn)閑xcel,PDF,png等格式無法轉(zhuǎn)json)),假如現(xiàn)在的需求是請(qǐng)求和返回參數(shù)均以json格式為主,并且記錄日志到數(shù)據(jù)庫(kù)(排除查詢?nèi)罩镜慕涌?因?yàn)椴樵內(nèi)罩静恍枰涗浀綌?shù)據(jù)庫(kù),這里涉及到AOP排除某個(gè)接口或者說是某個(gè)切面,具體見下)
日志實(shí)體(表)
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>
* 日志記錄監(jiān)控表
* </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;
/**
* 請(qǐng)求IP地址
*/
private String requestIp;
/**
* 操作接口反饋描述
*/
private String summarize;
/**
* 響應(yīng)狀態(tài)嗎
*/
private Integer responseCode;
/**
* 請(qǐng)求方式
*/
private String requestType;
/**
* 請(qǐng)求接口
*/
private String requestApi;
/**
* 請(qǐng)求參數(shù)
*/
private String requestPara;
/**
* 返回參數(shù)
*/
private String responsePara;
/**
* 請(qǐng)求到響應(yīng)處理時(shí)間(毫秒)
*/
private Integer responseTime;
/**
* 創(chuàng)建時(shí)間(請(qǐng)求發(fā)出的時(shí)間)
*/
private Date createTime;
/**
* 日志所屬(0移動(dòng)前端日志,1PC端日志)
*/
private Integer belongTo;
}
下面是json格式并帶有記錄到數(shù)據(jù)庫(kù)的需求的,假如返回的格式是三段式j(luò)son,如下
{
"code": 200,
"msg": "查詢打卡次數(shù)成功",
"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 {
/**
* 記錄日志到數(shù)據(jù)庫(kù)
*/
@Autowired
private LogMonitorDao logMonitorDao;
//記錄到數(shù)據(jù)庫(kù)
LogMonitor logMonitor = new LogMonitor();
long start = 0;
/**
* 使用org.slf4j.Logger,這是Spring實(shí)現(xiàn)日志的方法
*/
private final static Logger logger = LoggerFactory.getLogger(AopJsonLog.class);
@Pointcut("execution(* com.xinzuo.lvyou.admin..*(..)) ")
public void myPointcut() {
}
/**
* 前置增強(qiáng) 單獨(dú)配置的前端接口切面
* 日志打印排除在外,查詢?nèi)罩窘涌诰筒挥涗洈?shù)據(jù)庫(kù)
* 排除某個(gè)方法使用!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("---------------------------------前端請(qǐng)求接口日志----------------------------------------------");
// 接收到請(qǐng)求,記錄請(qǐng)求內(nèi)容(這里同樣可以在前置增強(qiáng)配置請(qǐng)求的相關(guān)信息)
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
logger.info("訪問的接口:" + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName());
try {
logger.info("請(qǐng)求入?yún)?" + new JSONArray(Arrays.asList(jp.getArgs())).toString());
} catch (Exception e) {
logger.info("請(qǐng)求入?yún)?圖片,視頻,excel,PDF等格式(此時(shí)無法轉(zhuǎn)換成JSON格式)");
//由于知道這里異常的原因是json轉(zhuǎn)換參數(shù)異常,所以就不打印了,不捕獲,以免控制臺(tái)難看或者日志難看
//e.printStackTrace();
}
//logger.info("請(qǐng)求的地址URL: " + request.getRequestURL().toString());
//logger.info("請(qǐng)求的方式HTTP_METHOD: " + request.getMethod());
//logger.info("請(qǐng)求的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("請(qǐng)求入?yún)?圖片,視頻,excel,PDF等格式(此時(shí)無法轉(zhuǎn)換成JSON格式)");
}
logMonitor.setRequestIp(request.getRemoteAddr());
logMonitor.setCreateTime(new Date());
}
/**
* 后置增強(qiáng)
* 排除某個(gè)方法使用!execution(* com.xinzuo.lvyou.controller.LogMonitorController.*(..))
* 日志打印排除在外,查詢?nèi)罩窘涌诰筒挥涗洈?shù)據(jù)庫(kù)
* @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());
//入?yún)⒋蛴son數(shù)組格式
// try {
// logger.info("請(qǐng)求入?yún)?" + new JSONArray(Arrays.asList(jp.getArgs())).toString());
// } catch (Exception e) {
// logger.info("請(qǐng)求入?yún)閳D片,視頻,excel,PDF等格式(此時(shí)無法轉(zhuǎn)換成JSON格式)");
// //由于知道這里異常的原因是json轉(zhuǎn)換參數(shù)異常,所以就不打印了,不捕獲,以免控制臺(tái)難看或者日志難看
// //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("進(jìn)行圖片,視頻,excel,PDF等格式操作(由于這個(gè)操作特殊一點(diǎn),所以后臺(tái)直接把返回狀態(tài)碼默認(rèn)為200,以實(shí)際為準(zhǔn))");
logMonitor.setResponseCode(200);
//e.printStackTrace();
}
logMonitor.setBelongTo(0);
logMonitor.setResponseTime((int) (end - start));
try {
logMonitorDao.insert(logMonitor);
} catch (Exception e) {
logger.info("AOP系統(tǒng)故障!");
}
}
// /**
// * 異常拋出增強(qiáng)
// *
// * @param jp
// * @param e
// */
// @AfterThrowing(pointcut = "myPointcut()", throwing = "e")
// public void afterThrowing(JoinPoint jp, RuntimeException e) {
// logger.error("異常增強(qiáng):" + jp.getSignature().getName().getClass().getSimpleName() + "方法發(fā)生異?!? + e + "】");
// }
/**
* 環(huán)繞增強(qiáng) :測(cè)試的時(shí)候finally的切面日志注釋不打印,因?yàn)槿罩径嗔朔炊缓谜{(diào)試,上線時(shí)再取消注釋
*
* @param jp
* @return
* @throws Throwable
*/
@Around("myPointcut()")
public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable {
logger.info("admin---------------------------------后臺(tái)請(qǐng)求接口日志----------------------------------------------");
logger.info("訪問的接口:" + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName());
//入?yún)⒋蛴son數(shù)組格式
try {
logger.info("請(qǐng)求入?yún)?" + new JSONArray(Arrays.asList(jp.getArgs())).toString());
} catch (Exception e) {
logger.info("請(qǐng)求入?yún)?圖片,視頻,excel,PDF等格式(此時(shí)無法轉(zhuǎn)換成JSON格式)");
//由于知道這里異常的原因是json轉(zhuǎn)換參數(shù)異常,所以就不打印了,不捕獲,以免控制臺(tái)難看或者日志難看
//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("請(qǐng)求入?yún)?" + new JSONArray(Arrays.asList(jp.getArgs())).toString());
logger.error(jp.getSignature().getName() + " 方法發(fā)生異常【" + e + "】");
throw e;
} finally {
//logger.info("訪問的接口: " + jp.getTarget().getClass().getName() + "."+jp.getSignature().getName());
//logger.info("請(qǐng)求入?yún)? "+ new JSONArray(Arrays.asList(jp.getArgs())).toString());
//logger.info("執(zhí)行 :" + jp.getSignature().getName() + "方法結(jié)束。");
}
}
}
------------------------------------下面是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>
到此這篇關(guān)于Springboot2 配置AOP日志的方法步驟的文章就介紹到這了,更多相關(guān)Springboot2 配置AOP日志內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring MVC下 bootStrap服務(wù)器分頁代碼
因?yàn)镾pring 對(duì)于ajax直接返回對(duì)象,到了WEB頁面就轉(zhuǎn)換成json 所以不需要使用JSON轉(zhuǎn)換封裝可以直接使用。接下來通過本文給大家分享Spring MVC下 bootStrap服務(wù)器分頁代碼,需要的的朋友參考下2017-03-03
手把手教你實(shí)現(xiàn)idea中配置國(guó)內(nèi)源
idea的國(guó)內(nèi)源配置十分重要,能夠提升程序開發(fā)的效率而且也是減少bug的一種有效防范,本文就來介紹一下idea中配置國(guó)內(nèi)源,具有一定的參考價(jià)值,感興趣的可以了解一下2023-07-07
Spring Security OAuth2 實(shí)現(xiàn)登錄互踢的示例代碼
這篇文章主要介紹了Spring Security OAuth2實(shí)現(xiàn)登錄互踢的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
springmvc配置線程池Executor做多線程并發(fā)操作的代碼實(shí)例
今天小編就為大家分享一篇關(guān)于springmvc配置線程池Executor做多線程并發(fā)操作的代碼實(shí)例,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03

