您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)Springboot2 如何去配置AOP日志,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
Spring boot2 配置AOP前置增強(qiáng),后置增強(qiáng),異常增強(qiáng),環(huán)繞增強(qiáng),最終增強(qiáng)
關(guān)于AOP切面相關(guān)具體概念不做過(guò)多闡述(概念弄懂有利于理解思想),這是配置AOP的各種增強(qiáng)日志,解決日志嵌套在業(yè)務(wù)代碼的麻煩和不科學(xué)
先來(lái)個(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)的無(wú)參構(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)椤?quot; + 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ā)生異?!?quot; + 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)ⅰ?quot; + 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等格式無(wú)法轉(zhuǎn)json)),假如現(xiàn)在的需求是請(qǐng)求和返回參數(shù)均以json格式為主,并且記錄日志到數(shù)據(jù)庫(kù)(排除查詢?nèi)罩镜慕涌?因?yàn)椴樵內(nèi)罩静恍枰涗浀綌?shù)據(jù)庫(kù),這里涉及到AOP排除某個(gè)接口或者說(shuō)是某個(gè)切面,具體見(jiàn)下)
日志實(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("訪問(wèn)的接口:" + 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í)無(wú)法轉(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í)無(wú)法轉(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("訪問(wèn)的接口: " + 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í)無(wú)法轉(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ā)生異?!?quot; + 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("訪問(wèn)的接口:" + 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í)無(wú)法轉(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("訪問(wèn)的接口:" + 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("訪問(wèn)的接口: " + 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日志就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。