背景:  

         在实际的开发中,如果我们有些重要的接口需要访问日志,需要 访问人的信息 ,工号,接口路径,接口具体信息,我们要是在接口里写一套重复的逻辑,会影响整体的美观,也会影响开发速率,如果重要的接口比较多,也不好维护,所以我们用到了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");

        logger.info("URL: {}", request.getRequestURL().toString());
        logger.info("HTTP请求类型: {}", request.getMethod());
        logger.info("执行方法: {}", joinPoint);
        logger.info("传递参数: {}", Arrays.toString(joinPoint.getArgs()));
        //logger.info("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) {}

如果访问这个接口记录到日志里面:

java 自定义注解作为参数_spring boot

这样aop的自定义日志就做好了,

可能有些东西不是很好,各位大牛有更好的思路可以多多评论!