原创

基于Spring AOP实现自定义注解

温馨提示:
本文最后更新于 2024年02月25日,已超过 332 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

我们日常开发中,经常使用注解来完成很多工作,比如使用@Transactional来声明事务,而不需要手动编写事务逻辑,给我们日常开发带来很大便利。

现在我这里有记录博客所有接口的访问日志的需求,比如谁(IP)在什么时候访问了什么接口。

我们当然可以在想记录所有接口都增加记录日志的方法,但这样显然增加了大量的共性代码,而且也增加了主流程的风险,如果日志记录出错可能会让主业接口务异常,这显然不是我们希望的。

此时我们就可以想到,能不能像声明式事务一样使用注解来声明一下,告知Spring这里需要记录日志,至于日志怎么记录,我业务接口是不需要关心的。接下来我们就来尝试一下:

1.我们来定义一个注解,用来声明记录业务日志

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    /** 接口的名称 */
    String value() default "";

    /** 当前日志记录位置 */
    String storage() default "console";

    String mysql = "MySQL";
}

@Target({ElementType.METHOD}):定义我们注解使用的位置,这里是方法上
@Retention(RetentionPolicy.RUNTIME):定义我们的自定义注解保留多久,这里是运行时都保留
value():自定义方法名,标明接口的名称
storage():自定义方法名,标明接口输出(存储)位置
这里面的方法不是必须的,可根据自己的需求来自定义,最简单的就是什么都不写

2.切面逻辑如下:

@Aspect
@Component
public class LogAspect {
    private final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Around("@annotation(log)")
    public Object writeLog(ProceedingJoinPoint point, Log log) throws Throwable {
        //先执行业务
        Object result = point.proceed();
        try {
            ServletRequestAttributes attributes = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());
            if (Objects.nonNull(attributes)) {
                HttpServletRequest request = attributes.getRequest();
                String ip = request.getHeader("x-real-ip");

                String value = log.value();

                if (Log.mysql.equals(log.storage())){
                    // 记录数据库
                } else {
                    logger.info("日志控台输出 {} 访问了 {}", ip, value);
                }
            }
        } catch (Exception e) {
            logger.error("日志记录出错! {}", e.getMessage());
        }
        return result;
    }
}

@Aspect:标明当前对象是一个界面逻辑
3.自定义注解使用方法如下,在接口上加上@Log(value = "下单接口")就可以了,当然valuestorage方法都可以不填直接写@Log也没问题:

@PostMapping("/order")
@Log(value = "下单接口")
public ResponseVO<String> order(Long goodsId, Long userId) {
    return orderService.order(goodsId, userId);
}

4.输出日志:

控台输出日志 **** 访问了 下单接口

如此,一个简单又实用的自定义注解功能就这样完成了,就是如此简单。在我们日常开发中有很多类似的场景都可以使用该方案解决,比如:

  • 记录指定方法出入参、执行时间等信息,可用于后期调优
  • 接口鉴权,对接口的请求进行安全性校验
  • 接口请求统计,自定义限流、快速熔断、服务降级等等
  • 指定方法共性入参校验
    ......
正文到此结束
本文目录