前面针对AOP的使用姿势和一些疑问进行了说明,这一篇则从应用的角度出发,看下AOP可以实现些什么样的效果
对于后端服务而言,一个日常的需求就是需要记录一些关键方法调用历史情况,用于分析接口的响应、问题定位排查等,属于比较常见的场景了
因此,我们希望可以针对某些接口,知道传入的参数时什么,谁调用的,返回了啥,耗时多少这些基本信息。显然这些属于公用的普适性需求,与方法本身的业务无关,如果直接在每个方法内部中加这个逻辑,就比较恶心了;为了最少的倾入性和通用性,正好可以使用AOP来实现这么一个功能
这个属于比较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; }
添加下测试代码,我们先创建两个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
一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
打赏 如果觉得我的文章对您有帮助,请随意打赏。