SpringAop 实现记录操作日志

SpringAop 实现记录操作日志


前言

系统操作日志是一个重复性的工作,直接在对应逻辑后面写也不好,后续添加也不方便,所以通过注解SpringAop的方式来实现


提示:使用的是 spring 框架

一、效果展示

这里列举几重常用的用法

// 参数解析:<.....> 是解析模板,可自己定义   
 // args 是获取入参数里面属性值  eg: #args.对象名.属性名  或  #args.属性名
 // return 是获取返回对象的属性值  eg: #return.对象名.属性名 或  #return.属性名
 // 高级用法: HashMap<String, Object> map = new HashMap<>(); map.put("data",new UserEntity());
 // return[data].username 获取返回值map里面的data键的值,拿到对象在获取用户名username

更多用法后面会提到

二、使用步骤

1.定义注解类

代码如下(示例): 更多参数可自行定义

@Target({
          
    ElementType.PARAMETER, ElementType.METHOD })
	@Retention(RetentionPolicy.RUNTIME)
	@Documented
	public @interface LogMessage {
          
   
	    /**
	     * 模块名称
	     * @return
	     */
	    String name() default "";
	    /**
	     * 操作类型
	     * @return
	     */
	    String type() default "";
	    /**
	     * 操作描述 表达式编写的地方
	     * @return
	     */
	    String desc() default "";
	}

2.定义注解解析类

代码如下(示例):

@Aspect
@Component
public class LogMessageAspect {
          
   
	// 记录日志
	private static final Logger logger = LoggerFactory.getLogger(LogMessageAspect.class);
	// 下面是区分获取返回值还是参数用的,可自行定义
    public final static String REPLACE_REGEX_ARG = "args\.";
    public final static String MATCHES_REGEX_ARG = "(.*)"+REPLACE_REGEX_ARG+"(.*)>";
    public final static String REPLACE_REGEX_RETURN = "return";
    public final static String MATCHES_REGEX_RETURN = "(.*)"+REPLACE_REGEX_RETURN+"(.*)>";
	@AfterReturning(pointcut = "@annotation(log)", returning = "result")
    public void doAfterReturning(JoinPoint point, LogMessage log, Object result){
          
   
        //拿到写表达式的参数
        String desc = log.desc();
        EvaluationContext context = new StandardEvaluationContext();
        if (StringUtils.isEmpty(log.desc())){
          
   
            return;
        }
        // 获取参数执行逻辑
        if (log.desc().matches(MATCHES_REGEX_ARG)){
          
   
            desc = desc.replaceAll(REPLACE_REGEX_ARG,"");
            Object[] args = point.getArgs();
            Method method = ((MethodSignature) point.getSignature()).getMethod();
            ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
            String[] params = parameterNameDiscoverer.getParameterNames(method);
            for (int i = 0; i < Objects.requireNonNull(params).length; i++) {
          
   
                context.setVariable(params[i], args[i]);
            }
        }
        // 获取返回值执行逻辑
        if (log.desc().matches(MATCHES_REGEX_RETURN)) {
          
   
            context.setVariable(REPLACE_REGEX_RETURN,result);
        }
        // 创建解析器解析方法 可定义  
        //ParserContext parserContext = new TemplateParserContext("{", "}");
        ParserContext parserContext = new TemplateParserContext("<", ">");
        //创建解析器
        SpelExpressionParser parser = new SpelExpressionParser();
        // 这里是为了解析失败不影响正常接口逻辑
        Object value = null;
        try {
          
   
            value = parser.parseExpression(desc,parserContext).getValue(context);
        } catch (Exception e) {
          
   
            logger.error("日志记录异常 {}",e.getMessage());
        }
        
        // dosomething 我这里简单打印下,可以插入到数据库
        //User user = SUtils.getLoginUser();
        logger.info("操作模块 {}",log.name());
        logger.info("操作类型 {}",log.type());
        // 这一步请根据自己框架获取当前操作人信息
        logger.info("操作人 {}",user.getName());
        logger.info("操作方法 {}",point.getSignature().getName() + "()");
        // 可以插入更多参数,这里不做过多阐述 IP,浏览器类型 等
        // saveLog
        
    }
}

打印详情

3.在接口上使用注解

@RestController
@RequestMapping("/test")
public class TestController{
          
   
	@LogMessage(name = "用户管理" , desc = "新增用户:<#args.user.username>",type = "INSERT")
    @PostMapping("/save")
    public AjaxResult save(UserEntity user)
    {
          
   
        return AjaxResult.success(...);
    }
    // 实例对象
	class UserEntity {
          
   
	    private Integer userId;
	    private String username;
	    //setter
	    //getter
	}
}

总结

做完上面两步,就可以简单的记录操作日志了,我们可以把操作日志插入到数据库中 这里使用的spring 的技术 SpEL ,表达式功能很强大,还能做if判断,基本满足你写日志的要求,更多用法请搜索

经验分享 程序员 微信小程序 职场和发展