【JavaAgent】字节码编程 - 方法运行时间实现

简介

使用JavaAgent实现一个简单的需求:方法的运行时间。这是一个很简单的例子,我们的目的是了解各种库之间的差异,做出正确的选择。

我们将学习:

  • 使用ASM, Javassist和byte-buddy库实现
  • 编写测试用例,运行查看效果
  • 设置环境变量切换实现

使用版本

  • JDK 11
  • asm 9.2
  • javassist 3.28
  • byte-buddy 1.12.3

目标代码开发

我们要确定目标类,也就是哪些类需要处理。本项目确定一个目标类 TargetClass.java, 代码如下:

@Slf4j
public class TargetClass {

    public void method1() throws InterruptedException{
        log.info(">>> method1 called ");
        Thread.sleep((new Random()).nextInt(1000));
    }

    public String method2(){
        log.info(">>> method2 called ");
        
        try {
            Thread.sleep((new Random()).nextInt(1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return "end";
    }
}

我们要了解这个类的方法运行时间,这里需要注意: method1 没有返回值。字节码编程时,这两个方法的处理有所不同。

我们还需要程序入口,并且创建了 TargetClass 实例,运行method1和method2方法,Main.java代码如下:

@Slf4j
public class Main {
    public static void main(String[] args) throws InterruptedException {
        log.info(">>> Main is running -> {}", Main.class.getName());

        TargetClass target = new TargetClass();
        target.method1();
        target.method2();
    }
}

运行程序,可以看见目标方法的执行情况。

字节码编程

我们需要一个功能统计运行时间,秒表(StopWatch.java)实现如下:

@Slf4j
public class StopWatch {
    /**
     * 
     * 静态方法实现
     *
     * @author PinWei Wan
     * @since 1.0.1
     */
    public static class StaticClazz {
        static ThreadLocal<Long> t = new ThreadLocal<Long>();
    
        public static void start() {
            t.set(System.currentTimeMillis());
        }
    
        public static void end() {
            final long elapseOfTime = System.currentTimeMillis() - t.get();
            log.info("{} elapse of time: {}", Thread.currentThread().getStackTrace()[2] , elapseOfTime);
    
            t.remove();          
        }
    
    }

    /**
     * 
     * 类实现
     *
     * @author PinWei Wan
     * @since 1.0.1
     */
    public static class Clazz {
        private final String methodName;
        private long start;
    
        public Clazz(String methodName) {
            this.methodName = methodName;
        }

        public  void start() {
            start = System.currentTimeMillis();
        }
    
        public void end() {
            final long elapseOfTime = System.currentTimeMillis() - start;
            log.info("{} elapse of time: {}", methodName , elapseOfTime);
       
        }
    
    }

}

这里有两种实现:类和静态类,为什么呢?字节码编程是很专业的,需要学习库的语法,使用静态类可以降低实现需求的难度。

我们先实现需求,对目标类方法编程,ASM库实现(TransformerWithASM.java):

public class TransformerWithASM implements Transformer{
    private final static String TARGET_CLASS = "io/github/kavahub/learnjava/TargetClass";

    @Override
    public void transform(String args, Instrumentation inst) {
        inst.addTransformer(new ClassFileTransformer() {

            @Override
            public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                    ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
                log.info("transform class - " + className);

                // 仅处理TargetClass类
                if (className.equals(TARGET_CLASS)) {
                    ElapseOfTimeClassWriter writer = new ElapseOfTimeClassWriter(classfileBuffer);
                    return writer.perform();
                }

                return ClassFi
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值