在很多系统开发中,我们希望在指定的方法调用之前或者之后能打印出该方法的调用时间以及方法的出参和入参,就可以使用spring的AOP,还可以结合自定义的注解进行进行一些指定参数的打印
例如:一个分层的架构系统,每层都有自己的指定系统名字,并且每个方法都有自己指定的作用(通过注解指定,在切面的时候取出该参数),而且可以根据注解的指定日志类型(在注解中指定,在切面的时候取出参数进行判断,然后打印相对应的日志格式)。
- 1.首先需要自定义注解:
systemName:表示该系统的名称。
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", "方法调用时间"),
PARAM("PARAM", "参数打印");
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(* *(..))
第一个* :表示所有返回值
第二个* :表示所有包匹配规则和所有类匹配规则以及所有方法匹配规则
两个点.. :表示任何参数匹配
例如:
execution(* *..*Service.*(..))
表示任何返回值 任何包以Service结尾的类或者实现类的任何方法任何参数
*.. :表示所有包
* :Service表示所有以Service结尾的类或者实现类
execution(* cn.lijie.MyService.*(..))
表示任何返回值 cn.lijie包下面 MyService类或者实现类的所有方法 所有参数
代码如下:
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("系统为:{},调用的方法名字:{},调用是否成功:{},运行时间为:{}ms", logAnnotation.systemName(), method, flag, totalTimeMillis);
}
private void paramLog(String method, ProceedingJoinPoint point, Object retValue) {
try {
String result = JSON.toJSONString(retValue);
//获取入参
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("调用方法为:{},入参为:{},出参为:{}", method, sb, subStr(result, 200));
} catch (Exception e) {
thisLog.warn("切面日志 参数日志打印异常,异常信息:{}", 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.定义一个操作对象:
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 = "计算器系统", bizCode = "+", logtype = LogType.TIME)
public int testLog01(int a, int b) {
logger.info("进入+运算");
return a + b;
}
@LogAnnotation(systemName = "计算器系统", bizCode = "-", logtype = LogType.PARAM)
public int testLog02(int a, int b) {
logger.info("进入-运算");
return a - b;
}
}
- 5.定义两个空类,用于区分不同类型的日志:
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>
最后执行测试的类,日志打印如下: