背景:
在实际的开发中,如果我们有些重要的接口需要访问日志,需要 访问人的信息 ,工号,接口路径,接口具体信息,我们要是在接口里写一套重复的逻辑,会影响整体的美观,也会影响开发速率,如果重要的接口比较多,也不好维护,所以我们用到了aop来实现 自定义日志记录
创建注解:
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented //生成文档
public @interface LogAnnotation {
String message(); // 日志内容
String operation(); // 日志类型
}
@Target(ElementType.METHOD)
:这个元注解用于指定LogAnnotation
这个自定义注解可以应用的地方。在这里,它被设置为ElementType.METHOD
,意味着该注解只能被应用于方法级别上。Java的ElementType
枚举定义了多种目标类型,比如TYPE
(类、接口)、FIELD
(字段)、METHOD
(方法)等。@Retention(RetentionPolicy.RUNTIME)
:这个元注解指定了LogAnnotation
注解的生命周期。RetentionPolicy.RUNTIME
意味着这个注解不仅在编译期被保留,还会在运行时保留,因此可以通过反射机制读取到这个注解的信息。Java的RetentionPolicy
有三种策略:SOURCE
(只在源码阶段保留,编译时丢弃)、CLASS
(编译时保留,在class文件中存在,但JVM加载时丢弃)、RUNTIME
(运行时保留,可通过反射获取)。@Documented
:这个元注解表明使用了LogAnnotation
的元素(在这里是方法)应该被包含在生成的JavaDoc文档中。这有助于开发者了解哪些方法或类使用了这个日志注解,以及其预期用途。public @interface LogAnnotation { ... }
:这部分定义了注解的结构。LogAnnotation
注解包含两个属性:
-
String message()
:表示日志的具体内容。当在方法上使用此注解时,开发者需要为这个属性提供一个字符串值。 -
String operation()
:表示日志的操作类型或类别,例如"ADD"、"UPDATE"、"DELETE"等,同样需要开发者在使用时指定一个字符串值。
编写aop:
@Aspect
@Component
public class SystemLogAspect {
//这个是我日志存放位置
@Autowired
UserUnifiedLogsService userLoginLogsService;
private static String header = "Authorization";
private static Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);
//定义切点@PointCut
//在注解位置切入代码
//这个annotation(里面填写创建注解的路径)
@Pointcut("@annotation(com.xxx.xxx.logs.LogAnnotation)")
public void logPoinCut() {
}
//前置通知
//在执行方法之前打印获取的参数内容
@Before("logPoinCut()")
public void before(JoinPoint joinPoint) throws UnsupportedEncodingException {
//我们项目会在请求头里面自带token 可以从token里取到工号和姓名
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String token = request.getHeader(header);
String badge = JWT.decode(token).getClaim("badge").asString();
String name = JWT.decode(token).getClaim("name").asString();
request.setCharacterEncoding("UTF-8");
("URL: {}", request.getRequestURL().toString());
("HTTP请求类型: {}", request.getMethod());
("执行方法: {}", joinPoint);
("传递参数: {}", Arrays.toString(joinPoint.getArgs()));
//("IP地址: {}: " + request.getRemoteAddr());
//获取方法签名对象
//从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//获取切入点所在的方法
Method method = signature.getMethod();
//获取操作
LogAnnotation myLog = method.getAnnotation(LogAnnotation.class);
//创建一个实体类,用于存储收到的信息,然后将他打包发给数据库
UserUnifiedLogs userLoginLogs = new UserUnifiedLogs();
userLoginLogs.setLoginTime(new Date());
userLoginLogs.setBadge(badge);
userLoginLogs.setUserName(name);
userLoginLogs.setUrl(request.getRequestURL().toString());
if (myLog!=null) {
userLoginLogs.setType(myLog.operation());
userLoginLogs.setMessage(myLog.message());
}
userLoginLogsService.insertLogs(userLoginLogs);
}
日志的实体类:
@Data
public class UserUnifiedLogs {
private Integer id;
private String userName;
private String badge;
private Date loginTime;
private String url;
private String type;
private String message;
}
在controller上面:
@LogAnnotation(message = "员工信息表", operation = "QUERY")
@Operation(summary = "员工信息表", description = "员工信息表,可以根据分页和工号去查询员工的信息")
public TableDataInfo initBasicList(@RequestBody PersonnelDto personnelDto) {}
如果访问这个接口记录到日志里面:
这样aop的自定义日志就做好了,
可能有些东西不是很好,各位大牛有更好的思路可以多多评论!