操作日志的处理,用户在使用系统时会有各种操作行为,比如登录、查询信息、创建内容、变更密码、更改权限等等,归属到后端系统,其实就是请求接口后,做增删改查。为了回溯,操作日志不可避免,也就是要记录用户的这些操作行为。
常见做法是使用自定义注解 + AOP 拦截,拿到访问 IP、业务名称、服务模块(时间是必须的)…
为了回顾一些概念,本篇博文,我来实现一个。
自定义注解
注解是一种能被编译器编译打包到 class 文件的元数据。我们常见的注解有两类:
由编译器识别使用的注解,这类很容易被忽略,因为是针对编译器,一般会自带生成,比如:override
,这类注解不会打包到 class 文件。不信,我们去项目 target
目录全局搜 override
,肯定搜不到。
第二类是项目代码运行时使用的注解,它们加载后一直存在 JVM 中,很多常见的注解,像 Spring 家族那些注解等等都是。这些注解本质还是手动使用 Java 代码实现功能,像之前我分享过的Spring笔记(03) SpringMVC底层实现模拟,这个过程就是自定义注解。
像现在,为了拦截接口,我们也要实现一个自定义注解,这里取名 BussinessLog
。
使用@interface
定义注解,注解里可以配置参数,如果要配置参数,有几个“潜规则”要注意。
- 有且必要配置一个
value
参数
- 给参数设置默认值
- 参数和普通类定义参数不一样,类似无参数方法
现在来看步骤:
第一步,使用@interface
定义注解
public @interface BussinessLog {
}
第二步,添加参数,配置默认值
public @interface BussinessLog {
//业务名称
String value() default "";
//服务模块
ServiceEnum service() default ServiceEnum.LOGIN;
//是否存入数据库
boolean save() default true;
}
第三步,用元注解配置
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BussinessLog {
//业务名称
String value() default "";
//服务模块
ServiceEnum service() default ServiceEnum.LOGIN;
//是否存入数据库
boolean save() default true;
}
aop
AOP 不仅可以用来切(拦截)方法,也可以切注解,也就是说切入点可以是方法或注解,像这里就是注解。
我们需要编写一个切面类,切面类和普通类区别不大,一般切面类常带俩注解,@Aspect
、@Component
,如果有用到日志,可能还会有@Slf4j
。
@Slf4j
@Aspect
@Component
public class BussinessLogAspect {
@Pointcut(value = "@annotation(com.***.BussinessLog)")
public void pointcut() {
}
@Around("pointcut()")
public Object writeLog(ProceedingJoinPoint point) throws Throwable {
//先执行业务
Object result = point.proceed();
try {
//处理日志,打印或存入数据库
handle(point);
} catch (Exception e) {
log.error("日志记录出错!", e);
}
return result;
}
}
如上代码有两点要注意,切入点Pointcut
填写BussinessLog
注解的路径,其次先执行任务,也就是先进入 Controller
,完成返回后再处理操作日志。一般要么打印输入到 log 文件,要么保存到数据库,分情况。
使用注解很简单,一般会放到 Controller
层方法上。
本文由老郭种树原创,转载请注明:https://guozh.net/custom-annotations-and-aop-processing-action-logs/