使用AOP实现一个接口监视器

Posted 寒冰护狐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用AOP实现一个接口监视器相关的知识,希望对你有一定的参考价值。

使用AOP实现一个接口监视器

一、背景

      在接口调试的过程中,很多时候会出现接口bug,在生产环境很难找到具体接口的参数,返回值,以及异常信息,此时我们就需要一个接口日志监听器,来保证我们接口调试过程中的便利性

二、开发思路

      编写接口信息监听器,我们可以采用filter与spring所提供的AOP,AOP相比filter更灵活一些,而且可以直接结合spring的其他方法来使用,扩展性也强

三、AOP

  • AOP即面向切面编程,简单的来说就是在普通的顺序执行的代码中,创造一个切点,然后在对切点进行前置通知,后置通知代码的描述,
  • @Aspect 声明切面,可以在类、接口、枚举中使用
  • @Pointcut 声明切点,用于关联切点,可使用表达式
  • @Before 前置通知,在切点执行之前运行
  • @After 后置通知,在切点执行之后运行
  • @AfterReturning 增强后置通知,可以获取切点的返回值
  • @AfterThrowing 异常通知,当接口发生错误时,进入异常通知,但是如果异常被捕获,则不会进入异常通知
  • @Around 环绕通知,会在切点执行前后分别执行,环绕通知的优先级大于前置通知以及后置通知

四、实现步骤

  • 第一步创建切面
@Aspect
@Component
public class UrlAspect {
	// 创建一个线程空间,来存放接口执行的开始毫秒数
    private ThreadLocal<Long> threadLocal = new ThreadLocal<>();
}
  • 第二步创建切点以及需要扫描的控制器
    @Pointcut("execution(* com.xc..controller.MonitorController.*(..))")
    public void logCut() {
    }
  • 第三步创建前置通知
    @Before("logCut()")
    public void beforeLog(JoinPoint joinPoint) throws IOException {// 保存接口开始执行的时间
        long startTime = System.currentTimeMillis();
        threadLocal.set(startTime);
    }
  • 第四步创建增强后置通知
    @AfterReturning(value = "logCut()", returning = "result")
    public void returningLog(JoinPoint joinPoint, Object result) {
    	// 获取HttpServletRequest对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 获取HttpServletResponse对象
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
		
		// 通过线程空间存储的接口开始时间计算接口耗时
        Long startTime = threadLocal.get();
        long endTime = System.currentTimeMillis();

		// 获取接口参数
        Object[] args = joinPoint.getArgs();

		// 将接口的请求响应信息存储到数据库中
        LogBean logBean = new LogBean();
        logBean.setRequestUrl(request.getRequestURL().toString())
                .setRequestType(request.getMethod())
                .setRequestParmeter(Arrays.stream(args).map(Object::toString).collect(Collectors.joining(",")))
                .setResponseStatus(String.valueOf(response.getStatus()))
                .setResponseResult(result.toString())
                .setResponseTime(String.valueOf(endTime - startTime));
        logService.addLog(logBean);
    }
  • 第五步创建异常通知
    @AfterThrowing(value = "logCut()", throwing = "exception")
    public void throwingLog(JoinPoint joinPoint, Throwable exception) {
    	// 获取HttpServletRequest对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 获取HttpServletResponse对象
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();

		// 通过线程空间存储的接口开始时间计算接口耗时
        Long startTime = threadLocal.get();
        long endTime = System.currentTimeMillis();

		// 获取接口参数
        Object[] args = joinPoint.getArgs();

		// 将接口的请求响应信息存储到数据库中
        LogBean logBean = new LogBean();
        logBean.setRequestUrl(request.getRequestURL().toString())
                .setRequestType(request.getMethod())
                .setRequestParmeter(Arrays.stream(args).map(Object::toString).collect(Collectors.joining(",")))
                .setResponseStatus(String.valueOf(response.getStatus()))
                .setResponseResult(exception.getMessage())
                .setResponseTime(String.valueOf(endTime - startTime));
        logService.addLog(logBean);
    }
  • 第六步创建控制器查看监听器的调用结果
@RestController
@RequestMapping("/monitor")
public class MonitorController {

    @GetMapping("/getRequset/{id}")
    public String getRequset(String name, @PathVariable("id") String id) {
        return "这里是GET请求";
    }

    @PutMapping("/putRequset")
    public String putRequset() {
        return "这里是PUT请求";
    }

    @PostMapping("/postRequset")
    public String postRequset() {
        return "这里是POST请求";
    }

    @DeleteMapping("/deleteRequset")
    public String deleteRequset() {
        return "这里是DELETE请求";
    }

}

在这里插入图片描述
在这里插入图片描述

五、总结

不知不觉已经毕业两年了,工作了这么久,才开始用这些面试经常用到的技术,略微有些惭愧,但是好过不学,现在还是需要保持经常学习的态度,我的技术还有待提高,我的目标是能开发出高可用的接口,拒绝死代码
需要该完整代码的小伙伴可以访问https://github.com/bugMakker/myWorkSpace/tree/xc

以上是关于使用AOP实现一个接口监视器的主要内容,如果未能解决你的问题,请参考以下文章

使用AOP实现一个接口监视器

spring-AOP原理

spring aop中this和target区别

spring中AOP

JAVA之AOP

手撸golang spring ioc/aop 之2