「字节码插桩」统计方法耗时(第一篇:初出茅庐)- 第311篇
一、准备工作
我们编写一个美眉类,有几个方法:
- package com.kfit.test;
-
- public class MeiMei {
-
- public void shopping() {
- System.out.println("shopping:出发去和美眉一起逛街购物!");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("shopping:和美眉一起回家!");
- }
-
- //求和计算.
- public double sum(double x,double y) {
- double sum = x+y;
- return sum;
- }
-
-
- public static void main(String[] args) {
- MeiMei meimei = new MeiMei();
- //和美眉一起购物.
- meimei.shopping();
- //计算下花了多少钱.
- double money = meimei.sum(1500, 3500);
- System.out.println("花了多少钱:"+money);
- }
-
- }
Run下代码执行结果如下:
shopping:出发去和美眉一起逛街购物!
shopping:和美眉一起回家!
花了多少钱:5000.0
二、统计方法耗时
2.1 利用源码计算时间统计耗时
我们在原先的代码包裹上一段时间统计的代码如下示例:
- long startTime = System.currentTimeMillis();
- //…… 执行具体的代码段
- long endTime = System.currentTimeMillis();
- System.out.println("耗时:"+(endTime-startTime));
师傅:徒儿,你看下上面的代码怎么修改呐?
徒儿,这个就很简单了,在每个方法的头部和结束加上上面的代码:
- package com.kfit.test;
-
- public class MeiMei1 {
-
- public void shopping() {
- long startTime = System.currentTimeMillis();
- System.out.println("shopping:出发去和美眉一起逛街购物!");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("shopping:和美眉一起回家!");
- long endTime = System.currentTimeMillis();
- System.out.println("shopping耗时:"+(endTime-startTime));
-
- }
-
- //求和计算.
- public double sum(double x,double y) {
- long startTime = System.currentTimeMillis();
- double sum = x+y;
- long endTime = System.currentTimeMillis();
- System.out.println("sum耗时:"+(endTime-startTime));
- return sum;
- }
-
-
- public static void main(String[] args) {
- MeiMei1 meimei = new MeiMei1();
- //和美眉一起购物.
- meimei.shopping();
- //计算下花了多少钱.
- double money = meimei.sum(1500, 3500);
- System.out.println("花了多少钱:"+money);
- }
-
- }
Run一下执行结果如下:
shopping:出发去和美眉一起逛街购物!
shopping:和美眉一起回家!
shopping耗时:1005
sum耗时:0
花了多少钱:5000.0
师傅:嗯,这个意思,那你说说你洗完的感觉。
徒儿:特别的不舒服,我这要是方法多,不得改废了。
师傅:这种方式,实现简单,但是确实实用性太差,有这么一些特点。
优点:实现简单,适用范围广泛;
缺点:侵入性强,大量的重复代码;
2.2 利用AutoCloseable
在 JDK1.7 引入了一个新的接口AutoCloseable, 通常它的实现类配合try{}使用,里面需要重写一个close方法,用来释放资源的,我们会常用于IO流的关闭。
- public class TimeConsumingAutoCloseable implements AutoCloseable{
- private long startTime;
-
- public TimeConsumingAutoCloseable() {
- this.startTime = System.currentTimeMillis();
- }
-
- /**
- * close方法在try里,释放资源的时候会调用,可以认为是在finally里执行的一个方法。
- */
- @Override
- public void close(){
- long endTime = System.currentTimeMillis();
- System.out.println("耗时:"+(endTime-startTime));
- }
- }
接下来之后,我们对于我们的美眉类稍微改造下:
- public class MeiMei2 {
-
- public void shopping() {
- System.out.println("shopping:出发去和美眉一起逛街购物!");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("shopping:和美眉一起回家!");
-
- }
-
- //求和计算.
- public double sum(double x,double y) {
- double sum = x+y;
- return sum;
- }
-
-
- public static void main(String[] args) {
- MeiMei2 meimei = new MeiMei2();
- //和美眉一起购物.
- try(TimeConsumingAutoCloseable tc = new TimeConsumingAutoCloseable()){
- meimei.shopping();
- }
- //计算下花了多少钱.
- try(TimeConsumingAutoCloseable tc = new TimeConsumingAutoCloseable()){
- double money = meimei.sum(1500, 3500);
- System.out.println("花了多少钱:"+money);
- }
- }
- }
这种方式的特点就是:
优点:简单,适用范围广泛,且适合统一管理;
缺点:有代码侵入、编码不够优雅、方法多处调用需多处编写不利于代码管理。
2.3 Spring AOP
在 Spring 生态下,可以借助 AOP 来拦截目标方法,统计耗时:
- // 定义切点,拦截所有满足条件的方法
- @Pointcut("execution(public * com.kfit.*.*(*))")
- public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
- long startTime = System.currentTimeMillis();
- try{
- return joinPoint.proceed();
- } finally {
- System.out.println("耗时: " + (System.currentTimeMillis() - startTime));
- }
- }
Spring AOP 的底层支持原理为代理模式,为目标对象提供增强功能;在 Spring 的生态体系下,使用 aop 的方式来统计方法耗时,可以说少侵入且实现简单,但是有以下几个问题:
(1)统计粒度为方法级别
(2)类内部方法调用无法生效
总的来说,这种方式比前面两种方式已经好用了很多。
2.4 Java Agent
除了上面介绍的两种方式,在代码上都是有侵入性的,如果你的项目已经打包了,不能修改代码,那么上面的方式就基本无法使用了,有没有一种方式可以是零侵入的呐。这个必须有,可以利用 Java Agent来实现。
在JDK1.5以后,我们可以使用agent技术构建一个独立于应用程序的代理程序(即为Agent),用来协助监测、运行甚至替换其他JVM上的程序。使用它可以实现虚拟机级别的AOP功能。
这个具体牵涉的会比较多,我们在之后的章节进行展开讲解。
购买完整视频,请前往:http://www.mark-to-win.com/TeacherV2.html?id=287