在咱们的实际开发中,多多少少会遇到统计一段代码片断的耗时的状况,咱们通常的写法以下git
long start = System.currentTimeMillis(); try { // .... 具体的代码段 } finally { System.out.println("cost: " + (System.currentTimeMillis() - start)); }
上面的写法没有什么毛病,可是看起来就不太美观了,那么有没有什么更优雅的写法呢?github
<!-- more -->spring
了解 Spring AOP 的同窗可能立马会想到一个解决方法,若是想要统计某个方法耗时,使用切面能够无侵入的实现,如ide
// 定义切点,拦截全部知足条件的方法 @Pointcut("execution(public * com.git.hui.boot.aop.demo.*.*(*))") public void point() { } @Around("point()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); try{ return joinPoint.proceed(); } finally { System.out.println("cost: " + (System.currentTimeMillis() - start)); } }
Spring AOP 的底层支持原理为代理模式,为目标对象提供加强功能;在 Spring 的生态体系下,使用 aop 的方式来统计方法耗时,能够说少侵入且实现简单,可是有如下几个问题学习
在 JDK1.7 引入了一个新的接口AutoCloseable
, 一般它的实现类配合try{}
使用,可在 IO 流的使用上,常常能够看到下面这种写法测试
// 读取文件内容并输出 try (Reader stream = new BufferedReader(new InputStreamReader(new FileInputStream("/tmp")))) { List<String> list = ((BufferedReader) stream).lines().collect(Collectors.toList()); System.out.println(list); } catch (IOException e) { e.printStackTrace(); }
注意上面的写法中,最值得关注一点是,不须要再主动的写stream.close
了,主要缘由就是在try(){}
执行完毕以后,会调用方法AutoCloseable#close
方法;ui
基于此,咱们就会有一个大单的想法,下一个Cost
类实现AutoCloseable
接口,建立时记录一个时间,close 方法中记录一个时间,并输出时间差值;将须要统计耗时的逻辑放入try(){}
代码块this
下面是一个具体的实现:代理
public static class Cost implements AutoCloseable { private long start; public Cost() { this.start = System.currentTimeMillis(); } @Override public void close() { System.out.println("cost: " + (System.currentTimeMillis() - start)); } } public static void testPrint() { for (int i = 0; i < 5; i++) { System.out.println("now " + i); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { try (Cost c = new Cost()) { testPrint(); } System.out.println("------over-------"); }
执行后输出以下:
now 0 now 1 now 2 now 3 now 4 cost: 55 ------over-------
若是代码块抛异常,也会正常输出耗时么?
public static void testPrint() { for (int i = 0; i < 5; i++) { System.out.println("now " + i); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } if (i == 3) { throw new RuntimeException("some exception!"); } } }
再次输出以下,并无问题
now 0 now 1 now 2 now 3 cost: 46 Exception in thread "main" java.lang.RuntimeException: some exception! at com.git.hui.boot.order.Application.testPrint(Application.java:43) at com.git.hui.boot.order.Application.main(Application.java:50)
除了上面介绍的两种方式,还有一种在业务开发中不太常见,可是在中间件、偏基础服务的功能组件中能够看到,利用 Java Agent 探针技术来实现,好比阿里的 arthas 就是在 JavaAgent 的基础上作了各类上天的功能,后续介绍 java 探针技术时会专门介绍
下面小结一下三种统计耗时的方式
基本写法
long start = System.currentTimeMillis(); try { // .... 具体的代码段 } finally { System.out.println("cost: " + (System.currentTimeMillis() - start)); }
优势是简单,适用范围普遍;缺点是侵入性强,大量的重复代码
Spring AOP
在 Spring 生态下,能够借助 AOP 来拦截目标方法,统计耗时
@Around("...") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); try{ return joinPoint.proceed(); } finally { System.out.println("cost: " + (System.currentTimeMillis() - start)); } }
优势:无侵入,适合统一管理(好比测试环境输出统计耗时,生产环境不输出);缺点是适用范围小,且粒度为方法级别,并受限于 AOP 的使用范围
AutoCloseable
这种方式能够看作是第一种写法的进阶版
// 定义类 public static class Cost implements AutoCloseable { private long start; public Cost() { this.start = System.currentTimeMillis(); } @Override public void close() { System.out.println("cost: " + (System.currentTimeMillis() - start)); } } // 使用姿式 try (Cost c = new Cost()) { ... }
优势是:简单,适用范围普遍,且适合统一管理;缺点是依然有代码侵入
说明
上面第二种方法看着属于最优雅的方式,可是限制性强;若是有更灵活的需求,建议考虑第三种写法,在代码的简洁性和统一管理上都要优雅不少,相比较第一种能够减小大量冗余代码
一灰灰的我的博客,记录全部学习和工做中的博文,欢迎你们前去逛逛
尽信书则不如,已上内容,纯属一家之言,因我的能力有限,不免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激
一灰灰 blog