字节码增强技术(Skywalking)

274 阅读2分钟

常见的Java字节码增强技术对比图

对比总结

特性JavassistASMByte BuddyCGLIB
易用性简单,接近源码难,需要了解字节码细节简单,流式 API较简单
性能较低最高较高,低于 ASM
灵活性极高较低
学习曲线平缓陡峭平缓平缓
支持范围动态生成、增强所有字节码操作动态生成、增强类代理,不支持接口代理
典型应用场景动态生成类,方法增强框架底层、高性能增强动态代理,类生成类增强(如事务代理)

选择建议

  • 性能优先:选择 ASM,适合框架开发或对性能有极高要求的场景。
  • 开发效率优先:选择 Byte Buddy 或 Javassist,适合日常开发或快速实现增强。
  • 简单增强:CGLIB(适合类代理)或 Javassist(快速操作类)。
  • 复杂逻辑增强:Byte Buddy 提供灵活性和简洁性,适合大多数增强需求。

Skywalking采用的就是Byte Buddy技术,依赖的核心jar包如下:

<dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy</artifactId>
</dependency>

<-- Agent 支持模块(如果需要通过 Java Agent 扩展运行时类)!-->
<-- skywalking就是使用该模式 !-->
<dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy-agent</artifactId>
</dependency>    

<-- ASM 支持模块(如果需要更底层的字节码增强功能 !-->
<dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy-asm</artifactId>
</dependency>

这里举一个使用Byte Buddy拦截某方法打印耗时案例:

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.matcher.ElementMatchers;

import java.lang.reflect.Method;

// 拦截器类,用于计算方法耗时
public class TimingInterceptor {
    public static Object intercept(Object proxy, Method method, Object[] args, net.bytebuddy.implementation.bind.annotation.SuperCall java.util.concurrent.Callable<?> callable) throws Exception {
        long startTime = System.currentTimeMillis();
        try {
            // 执行原始方法
            return callable.call();
        } finally {
            long endTime = System.currentTimeMillis();
            System.out.println("Method [" + method.getName() + "] executed in " + (endTime - startTime) + "ms");
        }
    }
}

// 被代理的目标类
class SampleClass {
    public void doWork() throws InterruptedException {
        System.out.println("Doing some work...");
        Thread.sleep(500); // 模拟耗时任务
    }
}

// 主类
public class ByteBuddyTimingExample {
    public static void main(String[] args) throws Exception {
        // 使用 Byte Buddy 动态代理目标类
        SampleClass proxy = new ByteBuddy()
            .subclass(SampleClass.class)
            .method(ElementMatchers.named("doWork")) // 匹配目标方法
            .intercept(MethodDelegation.to(TimingInterceptor.class).andThen(SuperMethodCall.INSTANCE)) // 拦截并调用原始方法
            .make()
            .load(SampleClass.class.getClassLoader())
            .getLoaded()
            .getDeclaredConstructor()
            .newInstance();

        // 调用代理后的方法
        proxy.doWork();
    }
}

执行结果:

Doing some work...
Method [doWork] executed in 500ms