• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

Spring Boot 应用篇–AOP:实现日志功能

Spring Boot winrains 来源:一灰灰Blog 12个月前 (11-08) 49次浏览

前面针对AOP的使用姿势和一些疑问进行了说明,这一篇则从应用的角度出发,看下AOP可以实现些什么样的效果

I. AOP实现日志拦截

1. 背景及目标

对于后端服务而言,一个日常的需求就是需要记录一些关键方法调用历史情况,用于分析接口的响应、问题定位排查等,属于比较常见的场景了
因此,我们希望可以针对某些接口,知道传入的参数时什么,谁调用的,返回了啥,耗时多少这些基本信息。显然这些属于公用的普适性需求,与方法本身的业务无关,如果直接在每个方法内部中加这个逻辑,就比较恶心了;为了最少的倾入性和通用性,正好可以使用AOP来实现这么一个功能

  • 拦截目标方法的执行
  • 打印请求参数,返回结果和执行时间到日志

2. 实现

这个属于比较aop的简单使用场景,因为需要知道返回结果,所有选择 around 或者 afterReturning advice;此外需要统计方法执行耗时,这样就只能选中 around
首先我们支持自定义注解方式,先定义一个注解,只要这个方法上有这个注解,就拦截

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnoDot {
}

其次,如果想更通用拦截指定包路径下的方法,可以如下定义PointCut;注意下面语句中的||表示或,只有有一个满足即可

@Pointcut("execution(public * com.git.hui.boot.aop.demo.*.*(..)) || @annotation(AnoDot)")
public void pointcut() {
}

接着就是我们的advice实现了

@Around(value = "pointcut()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    Object res = null;
    String req = null;
    long start = System.currentTimeMillis();
    try {
        req = buildReqLog(proceedingJoinPoint);
        res = proceedingJoinPoint.proceed();
        return res;
    } catch (Throwable e) {
        res = "Un-Expect-Error";
        throw e;
    } finally {
        long end = System.currentTimeMillis();
        System.out.println(req + "" + JSON.toJSONString(res) + SPLIT_SYMBOL + (end - start));
    }
}
private String buildReqLog(ProceedingJoinPoint joinPoint) {
    // 目标对象
    Object target = joinPoint.getTarget();
    // 执行的方法
    Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
    // 请求参数
    Object[] args = joinPoint.getArgs();
    StringBuilder builder = new StringBuilder(target.getClass().getName());
    builder.append(SPLIT_SYMBOL).append(method.getName()).append(SPLIT_SYMBOL);
    for (Object arg : args) {
        builder.append(JSON.toJSONString(arg)).append(",");
    }
    return builder.substring(0, builder.length() - 1) + SPLIT_SYMBOL;
}

3. 测试

添加下测试代码,我们先创建两个bean

// 这个bean下的方法,演示注解拦截
// com.git.hui.boot.aop.anodemo.AnoDemo
@Component
public class AnoDemo {
    @AnoDot
    public String gen(String ans) {
        return UUID.randomUUID() + "<>" + ans;
    }
}
// 这个bean下的方法,演示正则方式的拦截
// 注意前面的参数为..,表示任意参数类型和个数的方法都会拦截
// com.git.hui.boot.aop.demo.PrintDemo
@Component
public class PrintDemo {
    public String genRand(int seed, String suffix) {
        return seed + UUID.randomUUID().toString() + suffix;
    }
}

启动类如下

@SpringBootApplication
public class Application {
    public Application(PrintDemo printDemo, AnoDemo anoDemo) {
        System.out.println(printDemo.genRand(10, "--一灰灰Blog"));
        System.out.println(anoDemo.gen("!23"));
    }
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

输出结果

com.git.hui.boot.aop.demo.PrintDemo|genRand|10,"--一灰灰Blog"|"10521195c0-3c2a-41d0-82f5-a41afad066b0--一灰灰Blog"|240
10521195c0-3c2a-41d0-82f5-a41afad066b0--一灰灰Blog
com.git.hui.boot.aop.anodemo.AnoDemo|gen|"!23"|"1e3438fe-e31f-4f75-8405-4ff7494f9c9c<>!23"|26
1e3438fe-e31f-4f75-8405-4ff7494f9c9c<>!23

作者:一灰灰Blog

来源:http://spring.hhui.top/spring-blog/2019/03/13/190313-SpringCloud%E5%BA%94%E7%94%A8%E7%AF%87%E4%B9%8BAOP%E5%AE%9E%E7%8E%B0%E6%97%A5%E5%BF%97%E5%8A%9F%E8%83%BD/


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (1)