java注解結(jié)合aspectj AOP進(jìn)行日志打印的操作
在很多系統(tǒng)開發(fā)中,我們希望在指定的方法調(diào)用之前或者之后能打印出該方法的調(diào)用時(shí)間以及方法的出參和入?yún)?,就可以使用spring的AOP,還可以結(jié)合自定義的注解進(jìn)行進(jìn)行一些指定參數(shù)的打印
例如:
一個(gè)分層的架構(gòu)系統(tǒng),每層都有自己的指定系統(tǒng)名字,并且每個(gè)方法都有自己指定的作用(通過注解指定,在切面的時(shí)候取出該參數(shù)),而且可以根據(jù)注解的指定日志類型(在注解中指定,在切面的時(shí)候取出參數(shù)進(jìn)行判斷,然后打印相對應(yīng)的日志格式)。
1.首先需要自定義注解:
systemName:表示該系統(tǒng)的名稱。
bizCode:表示具體的方法描述
logtype:表示日志的格式類型
package myspring;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LogAnnotation {
String systemName();
String bizCode();
LogType logtype() default LogType.TIME;
}
2.定義日志格式枚舉:
package myspring;
public enum LogType {
TIME("TIME", "方法調(diào)用時(shí)間"),
PARAM("PARAM", "參數(shù)打印");
private String type;
private String desc;
LogType(String type, String desc) {
this.type = type;
this.desc = desc;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
3.切面代碼:
其中execution(* *(..))
第一個(gè)* :表示所有返回值
第二個(gè)* :表示所有包匹配規(guī)則和所有類匹配規(guī)則以及所有方法匹配規(guī)則
兩個(gè)點(diǎn).. :表示任何參數(shù)匹配
例如:
execution(* *..*Service.*(..))
表示任何返回值 任何包以Service結(jié)尾的類或者實(shí)現(xiàn)類的任何方法任何參數(shù)
*.. :表示所有包
* :Service表示所有以Service結(jié)尾的類或者實(shí)現(xiàn)類
execution(* cn.lijie.MyService.*(..))
表示任何返回值 cn.lijie包下面 MyService類或者實(shí)現(xiàn)類的所有方法 所有參數(shù)
代碼如下:
package myspring;
import com.alibaba.fastjson.JSON;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
@Component
@Aspect
public class LogAspect {
private static Logger thisLog = LoggerFactory.getLogger(LogAspect.class);
private static Logger timeLog = LoggerFactory.getLogger(TimeTypeLog.class);
private static Logger paramLog = LoggerFactory.getLogger(ParamTypeLog.class);
@Around("execution(* *(..)) && @annotation(logAnnotation)")
public Object log(ProceedingJoinPoint point, LogAnnotation logAnnotation) {
StopWatch stop = new StopWatch();
stop.start();
boolean flag = false;
Object retValue = null;
try {
retValue = point.proceed();
flag = true;
} catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
stop.stop();
if (logAnnotation.logtype().equals(LogType.TIME)) {
timeLog(this.getMethod(point), point, stop.getTotalTimeMillis(), logAnnotation, flag);
} else {
paramLog(this.getMethod(point), point, retValue);
}
}
return retValue;
}
private void timeLog(String method, ProceedingJoinPoint point, long totalTimeMillis, LogAnnotation logAnnotation, boolean flag) {
timeLog.info("系統(tǒng)為:{},調(diào)用的方法名字:{},調(diào)用是否成功:{},運(yùn)行時(shí)間為:{}ms", logAnnotation.systemName(), method, flag, totalTimeMillis);
}
private void paramLog(String method, ProceedingJoinPoint point, Object retValue) {
try {
String result = JSON.toJSONString(retValue);
//獲取入?yún)?
Object[] args = point.getArgs();
StringBuffer sb = new StringBuffer();
for (Object obj : args) {
String str = JSON.toJSONString(obj);
sb.append(subStr(str, 200)).append(" ");
}
paramLog.info("調(diào)用方法為:{},入?yún)?{},出參為:{}", method, sb, subStr(result, 200));
} catch (Exception e) {
thisLog.warn("切面日志 參數(shù)日志打印異常,異常信息:{}", e.getMessage());
}
}
private String getMethod(ProceedingJoinPoint pjp) {
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
return methodSignature.getDeclaringTypeName() + "#" + methodSignature.getMethod().getName();
}
private String subStr(String string, int length) {
if (!StringUtils.isEmpty(string)) {
if (string.length() > length) {
string = string.substring(0, 200);
return string;
}
}
return string;
}
}
4.定義一個(gè)操作對象:
package myspring;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component("logTest")
public class LogTest {
private static Logger logger = LoggerFactory.getLogger(LogTest.class);
@LogAnnotation(systemName = "計(jì)算器系統(tǒng)", bizCode = "+", logtype = LogType.TIME)
public int testLog01(int a, int b) {
logger.info("進(jìn)入+運(yùn)算");
return a + b;
}
@LogAnnotation(systemName = "計(jì)算器系統(tǒng)", bizCode = "-", logtype = LogType.PARAM)
public int testLog02(int a, int b) {
logger.info("進(jìn)入-運(yùn)算");
return a - b;
}
}
5.定義兩個(gè)空類,用于區(qū)分不同類型的日志:
package myspring;
public class TimeTypeLog {
package myspring;
public class ParamTypeLog {
}
6.spring xml配置文件:
<context:component-scan base-package="myspring"/> <aop:aspectj-autoproxy/>
7.啟動spring的測試類:
package myspring;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
LogTest logTest = (LogTest) context.getBean("logTest");
logTest.testLog01(1, 2);
logTest.testLog02(3, 4);
context.registerShutdownHook();
}
}
8.pom
<properties>
<spring_version>4.3.8.RELEASE</spring_version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.11</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.11</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.4</version>
</dependency>
</dependencies>
最后執(zhí)行測試的類,日志打印如下:

補(bǔ)充:spring boot 自定義注解實(shí)現(xiàn)自動打印方法日志(入?yún)⒑头祷刂担?/strong>
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.aline</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
定義一個(gè)日志注解
SysLog.java
package com.aline.demo.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
}
定義一個(gè)日志實(shí)例緩存
LoggerCache.class
package com.aline.demo.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
public class LoggerCache {
/**
* 日志實(shí)例記錄在內(nèi)存中
*/
private static HashMap<String, Logger> LOGERS = new HashMap<String, Logger>();
/**
* 根據(jù)類名獲取緩存的日志實(shí)例
* @param className 包名加類名 this.getClass().getName();
* @return
*/
public static Logger getLoggerByClassName(String className) {
// 從靜態(tài)map中獲取日志實(shí)例
Logger logger = LOGERS.get(className);
// 如果沒取到
if (logger == null) {
// 創(chuàng)建一個(gè)日志實(shí)例
logger = LoggerFactory.getLogger(className);
// 加入到靜態(tài)map中
LOGERS.put(className, logger);
}
// 返回
return logger;
}
}
定義一個(gè)切面實(shí)現(xiàn)日志記錄
SysLogAspect.java
package com.aline.demo.aspect;
import com.alibaba.fastjson.JSON;
import com.aline.demo.util.LoggerCache;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class SysLogAspect {
@Pointcut("@annotation(com.aline.demo.annotation.SysLog)")
public void log() {
}
/**
* 加入注解自動記錄方法日志
*
* @param joinPoint
* @return
* @throws Throwable
*/
@Around(value = "log()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
// 獲取執(zhí)行方法的類的名稱(包名加類名)
String className = joinPoint.getTarget().getClass().getName();
// 獲取實(shí)例和方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 從緩存中獲取日志實(shí)例
Logger log = LoggerCache.getLoggerByClassName(className);
// 記錄日志
log.info(className + "." + method.getName() + "() 執(zhí)行");
Object[] args = joinPoint.getArgs();
log.info("Params\t===》\t" + JSON.toJSONString(args));
// 執(zhí)行方法獲取返回值
Object proceed = joinPoint.proceed();
// 記錄日志
log.info("Returns\t===》\t" + JSON.toJSONString(proceed));
// 返回
return proceed;
}
}
寫個(gè)controller測試
TestController.java
package com.aline.demo.controller;
import com.aline.demo.annotation.SysLog;
import com.aline.demo.util.LoggerCache;
import org.slf4j.Logger;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;
@RestController
@RequestMapping("/test")
public class TestController {
static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
@GetMapping("/now")
public String now(){
// 從緩存中獲取日志
Logger LOG = LoggerCache.getLoggerByClassName(this.getClass().getName());
LOG.info("自定義日志 ==》 日志內(nèi)容。。。");
return sdf.format(new Date());
}
@GetMapping("/hello")
@SysLog()
public String hello(String name){
return "Hello, " + name;
}
}
訪問 http://localhost:8080/test/hello?name=aline
打印日志:
2019-05-09 16:58:20.410 INFO 40004 --- [nio-8080-exec-1] c.aline.demo.controller.TestController : com.aline.demo.controller.TestController.hello() 執(zhí)行,參數(shù) ==》 2019-05-09 16:58:20.584 INFO 40004 --- [nio-8080-exec-1] c.aline.demo.controller.TestController : Params ===》 ["aline"] 2019-05-09 16:58:20.598 INFO 40004 --- [nio-8080-exec-1] c.aline.demo.controller.TestController : Returns ===》 "Hello, aline"
訪問 http://localhost:8080/now
打印日志:
2019-05-09 16:58:29.208 INFO 40004 --- [nio-8080-exec-3] c.aline.demo.controller.TestController : 自定義日志 ==》 日志內(nèi)容。。。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
Spring MVC集成springfox-swagger2構(gòu)建restful API的方法詳解
這篇文章主要給大家介紹了關(guān)于Spring MVC集成springfox-swagger2構(gòu)建restful API的相關(guān)資料,文中介紹介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧。2017-06-06
Java中Spring技巧之?dāng)U展點(diǎn)的應(yīng)用
這篇文章主要介紹了Java中Spring技巧之?dāng)U展點(diǎn)的應(yīng)用,下文Spring容器的啟動流程圖展開其內(nèi)容的相關(guān)資料,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-04-04
SpringBoot集成分頁插件PageHelper的配置和使用過程
這篇文章主要介紹了SpringBoot集成分頁插件PageHelper的配置和使用過程,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04
SpringCloud網(wǎng)關(guān)組件Gateway原理深度解析
Spring Cloud Gateway是Spring Cloud微服務(wù)生態(tài)下的網(wǎng)關(guān)組件,一些基礎(chǔ)的請求預(yù)處理的邏輯可以統(tǒng)一實(shí)現(xiàn)在網(wǎng)關(guān)這一層,這樣業(yè)務(wù)服務(wù)只需要專注于處理業(yè)務(wù)邏輯即可,所以本文就帶大家深度解析網(wǎng)關(guān)組件Gateway,需要的朋友可以參考下2023-07-07
SpringBoot使用前綴樹實(shí)現(xiàn)敏感詞過濾示例
最近項(xiàng)目用到了敏感詞過濾,本文主要就來介紹一下SpringBoot使用前綴樹實(shí)現(xiàn)敏感詞過濾示例,具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10
MyBatis Plus 實(shí)現(xiàn)多表分頁查詢功能的示例代碼
這篇文章主要介紹了MyBatis Plus 實(shí)現(xiàn)多表分頁查詢功能,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
詳解使用SSM實(shí)現(xiàn)簡單工作流系統(tǒng)之實(shí)現(xiàn)篇
這篇文章主要介紹了使用SSM實(shí)現(xiàn)簡單工作流系統(tǒng)之實(shí)現(xiàn)篇,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-12-12

