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 代理,并在方法执行前打印一条消息。