基于spring 切面,切controller层的操作日志
如果管理系统想记录一些操作日志,但是又不想每个方法都记录(比如只记录增删改等操作),可以用aop切面记录日志,本文对controller层各种方式参数输入(包括文件上传)都有介绍
1、先定义注解,这样只对加注解的方法保存日志
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented
public @interface OperateLog {
//操作类型
String operateType() default "";
//操作描述
String description() default "";
}
2、在controller层中要切的方法上加入注解
@RequestMapping(value = "/update", method = RequestMethod.POST)
@OperateLog(operateType = "****", description = "xxxxxx")
Result<Void> update(@RequestBody @Valid DTO dto) {
Result<Void> result = service.update(dto);
return result;
}
3、编写切面日志保存类
@Aspect //切面标识类
@Component
public class OperateLogAspect {
//日志,也可以用lombok注解代替
public static Logger LOGGER = LoggerFactory.getLogger(OperateLogAspect.class);
@Autowired
private IOperateLogService operateLogService;
/**
* 设置操作日志切入点,在注解的位置切入代码
*/
@Pointcut("@annotation(***.common.annotation.OperateLog)")
public void LogPointCut() {
}
/**
* 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行,如果抛出异常,则不会执行
* @param joinPoint 切入点
*/
@AfterReturning(value = "LogPointCut()", returning = "keys")
public void saveOperateLog(JoinPoint joinPoint, Object keys){
LOGGER.info("开始保存操作日志");
//获取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//从RequestAttributes中获取HttpServletRequest的信息
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
//获取请求uri
String uri = request.getRequestURI();
Object paramObj = null;
try {
//获取请求参数
Object[] args = joinPoint.getArgs();
//自己定义的日志保存类
OperateLogDTO operateLogDTO = new OperateLogDTO();
//从切面织入点处通过反射机制获取切入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//获取切入点所在的方法
Method method = signature.getMethod();
//接口特殊处理,参数在HttpServletRequest里面
if(OperateLogAspect.SPECIAL_URI.equals(uri)) {
paramObj = WebUtil.getRequestParamMap(request);
} else {
//判断是否有上传文件
if(ServletFileUpload.isMultipartContent(request)) {
paramObj = getParamString(args);
} else {
paramObj = getParameter(method, args);
}
}
//获取注解的操作
OperateLog operateLog = method.getAnnotation(OperateLog.class);
//填充日志
if(operateLog != null) {
String operateType = operateLog.operateType();
String description = operateLog.description();
operateLogDTO.setOperateType(operateType);
operateLogDTO.setRemark(description);
}
operateLogDTO.setUserName(getUserName());
operateLogDTO.setUri(uri);
operateLogDTO.setOperateDate(new Date());
operateLogDTO.setRequestParameter(JSONObject.toJSONString(paramObj));
//日志保存入库
operateLogService.save(operateLogDTO);
} catch (CustomizeException e) {
LOGGER.error("操作 {} 日志插入数据库失败: {}", uri, e);
e.printStackTrace();
}catch (Exception e) {
LOGGER.error("操作 {} 日志插入数据库失败 {}", uri, e);
e.printStackTrace();
}
}
}
/**
* 转换操作日志为json字符串
* @param objects
* @return
*/
private String getParamString(Object[] objects) {
//如果有导入文件,去掉文件内容,只保存必要的信息
List<Object> list = new ArrayList<>();
String paramString = JSONObject.toJSONString(objects);
JSONArray jsonArray = JSONArray.parseArray(paramString);
Object[] paramObj = jsonArray.stream().toArray();
for(Object argObject : paramObj) {
JSONObject jsonObject = (JSONObject) argObject;
//如果是接口参数里面直接传的文件
if(jsonObject.containsKey("originalFilename")){
jsonObject.remove("bytes");
jsonObject.remove("contentType");
}
//如果是对象里面传的文件,比如对象里面文件属性名叫 file
if(jsonObject.containsKey("file")) {
JSONObject fileObj = (JSONObject) jsonObject.get("file");
fileObj.remove("bytes");
fileObj.remove("contentType");
}
list.add(jsonObject);
}
paramString = JSONObject.toJSONString(list);
return paramString;
}
/**
* 根据方法和传入的参数获取请求参数
* @param method
* @param args
* @return
*/
private List<Object> getParameter(Method method, Object[] args) {
List<Object> argList = new ArrayList<>();
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
//将RequestBody注解修饰的参数作为请求参数
RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
if (requestBody != null) {
argList.add(args[i]);
}
//将RequestParam注解修饰的参数作为请求参数
RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
if (requestParam != null) {
Map<String, Object> map = new HashMap<>();
String key = parameters[i].getName();
if (!StringUtils.isEmpty(requestParam.value())) {
key = requestParam.value();
}
map.put(key, args[i]);
argList.add(map);
}
}
//ModelAttribute注解修饰的和没有加参数注解的方法
if(argList.isEmpty()){
return Arrays.asList(args.clone());
}
return argList;
}
}