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判断,基本满足你写日志的要求,更多用法请搜索