基于spring 切面,切controller层的操作日志

基于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;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值