bytebuddy实战5 AgentBuilder.Transformer

29 阅读2分钟

AgentBuilder.Transformer 是 Byte Buddy 库中的一个接口,用于在 Java 代理中进行类的转换。Byte Buddy 是一个用于生成、修改和操作 Java 字节码的库,广泛用于创建 Java 代理、字节码增强和其他字节码操作。

AgentBuilder.Transformer 接口允许你定义如何转换类的字节码,这在创建 Java 代理时非常有用。通过实现这个接口,你可以插入自定义的字节码操作,例如方法拦截、注入字段或改变类的结构。

使用示例1、 bytebuddy实现javaanget

0、待拦截的类

public class UserManager {

    public UserManager() {
        System.out.println("UserManager init");
    }

    public String selectUserName(Long id) {
        return "hello" + id;
    }
}

1、agent 入口类

public class AgentDemo {
    public static void premain(String args , Instrumentation instrumentation){
        AgentBuilder.Identified.Extendable builder = new AgentBuilder.Default()
                .ignore(ElementMatchers.nameStartsWith("net.bytebuddy"))// 忽略的类,不拦截
                //要拦截的类
                .type(ElementMatchers.named("com.roadjava.bytebuddy.UserManager"))
                //指定转换类Transform
                .transform(new AgentTransform());
        //用于将构建的字节码安装到一个 Instrumentation 实例上
        builder.installOn(instrumentation);
    }
}

2、transform 实现类

public class AgentTransform implements AgentBuilder.Transformer {
    /**
     * 当要被拦截的type第一次被加载的时候会进入到此方法
     * @param builder
     * @param typeDescription 要被加载的类的信息
     * @param classLoader
     * @param javaModule
     * @return
     */
    @Override
    public DynamicType.Builder<?> transform(
            DynamicType.Builder<?> builder
            , TypeDescription typeDescription
            , ClassLoader classLoader
            , JavaModule javaModule) {
        DynamicType.Builder.MethodDefinition.ReceiverTypeDefinition<?> intercept =
                //要拦截的方法
                builder.method(ElementMatchers.not(ElementMatchers.isStatic()))
                //代理类
                .intercept(MethodDelegation.to(new UserManagerInterceptor()));
        return intercept;
    }
}

3、拦截器类

public class UserManagerInterceptor {
    @RuntimeType
    public Object init( @This Object targetObj,@Origin Method targetMethod,
                      @AllArguments Object[] targetMethodArgs,@SuperCall Callable<?> zuper) {
        long start = System.currentTimeMillis();
        Object call = null;
        try {
            call = zuper.call();
        } catch (Exception e) {
        }finally {
            System.out.println("耗时"+(System.currentTimeMillis()-start));
        }
        return call;
    }
}

使用示例2

以下是一个简单的示例,展示如何使用 AgentBuilder.Transformer 来修改类的字节码。

1. 添加 Byte Buddy 依赖

首先,你需要在项目中添加 Byte Buddy 的依赖。对于 Maven 项目,可以在 pom.xml 中添加以下依赖:

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

2. 实现 AgentBuilder.Transformer

接下来,创建一个实现 AgentBuilder.Transformer 接口的类。在这个示例中,我们将拦截一个类的所有方法并在方法执行前打印一条消息。

import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;

import java.lang.instrument.Instrumentation;

public class MyAgent {

    public static void premain(String arguments, Instrumentation instrumentation) {
        new AgentBuilder.Default()
                .type(ElementMatchers.any()) // 匹配所有类
                .transform(new AgentBuilder.Transformer() {
                    @Override
                    public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder,
                                                            TypeDescription typeDescription,
                                                            ClassLoader classLoader,
                                                            JavaModule module) {
                        return builder.visit(Advice.to(MyAdvice.class).on(ElementMatchers.any()));
                    }
                })
                .installOn(instrumentation);
    }

    public static class MyAdvice {
        @Advice.OnMethodEnter
        public static void onEnter() {
            System.out.println("方法调用前");
        }
    }
}

3. 创建代理 JAR

要创建一个 Java 代理,你需要指定 Manifest 文件中的 Premain-Class 属性。创建一个名为 MANIFEST.MF 的文件,内容如下:

Manifest-Version: 1.0
Premain-Class: MyAgent

然后使用以下命令打包 JAR 文件:

jar cmf MANIFEST.MF my-agent.jar MyAgent.class

4. 使用代理

最后,在运行 Java 应用程序时,使用 -javaagent 选项来加载代理 JAR:

java -javaagent:my-agent.jar -jar your-application.jar

总结

AgentBuilder.Transformer 是 Byte Buddy 库中的一个强大工具,用于在 Java 代理中进行类的转换。通过实现这个接口,你可以自定义类的字节码操作,实现方法拦截、注入字段或改变类的结构等功能。上述示例展示了如何使用 Byte Buddy 创建一个简单的 Java 代理,并在方法执行前打印一条消息。