设计一个基于注解的AOP日志记录功能
1.新建一个自定义注解OperatorLog.java
import java.lang.annotation.*;
/**
* 自定义注解
*/
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented //生成文档
public @interface OperatorLog {
String value() default "";
}
2.编写AOP日志记录SysLogAspect.java
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.base.controller.base.BaseController;
import com.base.repository.system.entity.OperatorLogEntity;
import com.base.service.system.IOperatorLogService;
import com.base.utils.IpUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
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.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author lzh
* @date 2019/8/5 - 17:25
*/
@Aspect
@Component
@Slf4j
public class SysLogAspect extends BaseController {
@Autowired
private IOperatorLogService operatorLogService;
//定义切点 @Pointcut
//在注解的位置切入代码
@Pointcut("@annotation( com.ysten.longquan.base.aspect.OperatorLog)")
public void logPoinCut() {
}
/**
* 计算接口请求耗时,要在Around环绕通知里边计算
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("logPoinCut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
OperatorLogEntity operatorLogEntity = new OperatorLogEntity();
//1、开始时间
long beginTime = System.currentTimeMillis();
//利用RequestContextHolder获取requst对象
ServletRequestAttributes requestAttr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = requestAttr.getRequest();
//请求URL
String uri = requestAttr.getRequest().getRequestURI();
operatorLogEntity.setRequestUrl(uri);
log.debug("URI: {}",uri);
//访问目标方法的参数 可动态改变参数值
Object[] args = joinPoint.getArgs();
//方法名获取
String methodName = joinPoint.getSignature().getName();
log.debug("请求方法:{}, 请求参数: {}", methodName, Arrays.toString(args));
//ip
log.debug("请求ip:{}", IpUtils.getIpAddr(request));
operatorLogEntity.setIp(IpUtils.getIpAddr(request));
//记录请求类型GET PUT POST DELETE
String httpMethod = request.getMethod();
operatorLogEntity.setRequestType(httpMethod);
Signature signature = joinPoint.getSignature();
if(!(signature instanceof MethodSignature)) {
throw new IllegalArgumentException("暂不支持非方法注解");
}
//调用实际方法
Object object = joinPoint.proceed();
//获取执行的方法
MethodSignature methodSign = (MethodSignature) signature;
Method method = methodSign.getMethod();
//判断是否包含了 无需记录日志的方法
OperatorLog logAnno = AnnotationUtils.getAnnotation(method, OperatorLog.class);
if(logAnno != null ) {
log.debug("log注解描述:{}", logAnno.value());
operatorLogEntity.setName(logAnno.value());
//日志细分为get/post/put/delete
Object[] reqArgs = joinPoint.getArgs();
String queryString = request.getQueryString();
String params = "";
//获取请求参数集合并进行遍历拼接
if(args.length>0){
if("POST".equals(httpMethod)){
Object obj = args[0];
Map map = getKeyAndValue(object);
params = JSON.toJSONString(map);
}else if("GET".equals(httpMethod)){
params = queryString;
}else if ("PUT".equals(httpMethod)){
Object obj = args[0];
Map map = getKeyAndValue(object);
params = JSON.toJSONString(map);
}else if ("DELETE".equals(httpMethod)){
params = queryString;
}
}
//参数保存
operatorLogEntity.setRequestParam(params);
//日志创建日期
operatorLogEntity.setCreateTime(new Date());
//记录操作用户名
operatorLogEntity.setUsername(getUserName());
operatorLogEntity.setCreateBy(getUserName());
operatorLogEntity.setUpdateBy(getUserName());
// result的值就是被拦截方法的返回值
Object result = joinPoint.proceed();
operatorLogEntity.setResponse(JSONObject.toJSONString(result));
log.debug("请求返回值:" + JSONObject.toJSONString(result));
//计算耗时
long endTime = System.currentTimeMillis();
long costTime = endTime - beginTime;
operatorLogEntity.setCostTime(costTime);
log.debug("结束计时: {}, URI: {},耗时:{}", new Date(),uri,costTime);
operatorLogService.save(operatorLogEntity);
return object;
}
return object;
}
public static Map getKeyAndValue(Object obj) {
Map map = new HashMap<>();
// 得到类对象
Class userCla = (Class) obj.getClass();
/* 得到类中的所有属性集合 */
Field[] fs = userCla.getDeclaredFields();
for (int i = 0; i < fs.length; i++) {
Field f = fs[i];
f.setAccessible(true); // 设置些属性是可以访问的
Object val = new Object();
try {
val = f.get(obj);
// 得到此属性的值
map.put(f.getName(), val);// 设置键值
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return map;
}
}
3.在要记录日志的接口上添加@OperatorLog注解