基于AOP切面实现日志响应

一、什么是AOP切面?

在JAVA中,AOP(面向切面编程)中的切面(Aspect)是一种编程概念,用于横切关注点(cross-cutting concerns)。横切关注点是指那些与业务逻辑无关的,但是会经常重复使用到的功能,例如日志响应,权限校验。

主要作用:

  • 模块化横切关注点:将横切关注点从业务逻辑代码中抽取出来,提高代码的可读性和可维护性
  • 减少代码冗余:避免在多个地方重复编写相同的代码
  • 提高代码复用性:切面可以在不同的运用程序上重复调用
  • 可插拔性:可以通过配置或者运行时添加切面,无需修改现有代码

核心概念:

  • 切面(Aspect):切面是一个模块化的横切关注点实现,它包括了连接点和通知。切面通常是一个类,里面可以定义切入点和通知。可以通过配置文件、注解等方式定义切面。比如我们这次编写的实现日志响应的这个class文件就是一个切面。
  • 连接点(Joinpoint):程序中能够被切面插入的点,典型的连接点包括方法调用、方法执行中某个时点等等,在Spring AOP中,连接点通常指的是方法调用。比如我们这次实现日志响应,所涉及到查看日志的包下的所有方法的调用点
  • 通知(Advice):在连接点处执行的代码。通知分为各种类型,包括前置通知(Before advice)、后置通知(After advice)、返回通知(After returning advice)、异常通知(After throwing advice)和环绕通知(Around advice)等。

  • 切点(PointCut):用于定义哪些连接点上应该运用的通知。切点通过表达式进行定义,如匹配所有public方法或匹配某个包下的所有方案等。比如我们这次实现controller包下所有方法日志响应。

二、编写代码

@Aspect //注解:这是一个AOP切面
@Component //标注这是一个springboot容器管理的组件,会自动扫描注册为spring Bean,自动注入依赖
@Slf4j
public class LogInterceptor {

    //执行拦截
    @Around("execution(* com.springbootinit.controller.*.*(..))")
    //@Around 注解表示在匹配的方法执行前后进行增强处理。
    //这段代码定义了一个切点表达式,用于拦截 com.springbootinit.controller 包及其子包下的所有方法。
    //切点:用于定义哪些连接点上应该运用的通知。
    //execution(* ...) 表示匹配方法的签名,* 表示返回值类型任意,.. 表示参数列表任意。
    public Object doInterceptor(ProceedingJoinPoint point) throws Throwable {
        //参数 joinPoint 表示被拦截的方法的连接点
        //连接点(Joinpoint):程序中能够被切面插入的点,典型的连接点包括方法调用、方法执行中某个时点等等,
        // 在Spring AOP中,连接点通常指的是方法调用
        // 计时
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        //stopWatch的start()方法是一个计时器的开始计时的方法,用于记录当前时间作为计时器的起始时间。
        //具体逻辑如下:
        //检查当前是否有正在运行的任务(currentTaskName != null),如果有则抛出异常,表示不能重复启动。
        //如果没有正在运行的任务,则记录当前任务名称和启动时间(使用纳秒级时间戳)

        // 获取请求路径
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        //RequestContextHolder.currentRequestAttributes()是一个用于获取当前请求的RequestAttributes对象的工具方法。
        HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();
        //将 RequestAttributes转换为 ServletRequestAttributes 类型,并通过其 getRequest() 方法获取 HttpServletRequest 对象。

        // 生成请求一个随机的、唯一 id(字符串),作为请求的唯一标识符
        String requestId = UUID.randomUUID().toString();
        String url = httpServletRequest.getRequestURI();
        // 获取请求参数
        Object[] args = point.getArgs();
        //获取拦截方法连接点的所有参数(没有固定的类型,就用Object数组)
        String reqParam = "[" + StringUtils.join(args, ", ") + "]";
        //使用 StringUtils.join 方法将数组元素用逗号拼接成字符串
        // 输出请求日志
        log.info("request start,id: {}, path: {}, ip: {}, params: {}", requestId, url,
                httpServletRequest.getRemoteHost(), reqParam);
        // 执行原方法
        Object result = point.proceed();//表示执行被拦截的目标方法
        // 输出响应日志
        stopWatch.stop();
        long totalTimeMillis = stopWatch.getTotalTimeMillis();
        log.info("request end, id: {}, cost: {}ms", requestId, totalTimeMillis);
        //放行
        return result;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值