.method(ElementMatchers.isPublic())
- 匹配名称为helloWorld且可见性为public的所有方法
.method(ElementMatchers.isPublic().and(ElementMatchers.named("helloWorld")))
- 匹配名称为helloWorld且可见性为public但返回值类型不为String的所有方法
.method(ElementMatchers.isPublic().and(ElementMatchers.named("helloWorld")).and(ElementMatchers.not(ElementMatchers.returns(String.class))))
- 匹配名称为helloWorld且第二个入参为String类型的所有方法
.method(ElementMatchers.isPublic().and(ElementMatchers.named("helloWorld")).and(ElementMatchers.takesArgument(1, String.class)))
可以看出这套匹配机制十分灵活,限定范围除了上述还包括注解,是否属于static等诸多其它维度,可以满足绝大部分场景的匹配需求,但也因为ElementMatcher太过灵活,匹配的时候注意不要误匹配到了不想增强的方法导致bug
匹配到了方法后就要对方法做增强,helloWorld的示例中的增强有些简陋,返回固定值没法满足大部分场景的需求,我们的需求大多是比较复杂的,这时候就可以依靠bytebuddy的MethodDelegation机制,将匹配到的方法"委派"给定义好的实现
In many scenarios, returning a fixed value from a method is of course insufficient. For more flexibility, Byte Buddy provides the MethodDelegation implementation which offers maximal freedom in reacting to method calls. A method delegation defines a method of the dynamically created type to forward any call to another method which may live outside of the dynamic type. This way, a dynamic class’s logic can be represented using plain Java while only the binding to another method is achieved by code generation
MethodDelegation
直接通过示例进行学习,我们定义一个Moo类,他有两个方法:
public class Moo {
public String Moo1(String param1) {return "parm1:" + param1;}
public String Moo(String param1, Integer param2) {return "param1:" + param1 + ",param2:"+ param2;}
这两个个方法都已经有默认的实现,我们现在定义一个“委托类”去覆盖他们的实现:
public class DelegateMoo {
public static String Moo(String param1, Integer param2) {
return "my name is " + param1 + ",my age is " + param2;
public static String Moo1(String param1) {
return "my name is " + param1;
委托类中有两个和Moo中很类似的方法,接下来我们调用bytebuddy的api去实现覆盖
@Test
public void testMethodDelegation() throws Exception {
Moo moo = new ByteBuddy()
.subclass(Moo.class)
.method(ElementMatchers.named("Moo").or(ElementMatchers.named("Moo1")))
.intercept(MethodDelegation.to(DelegateMoo.class))
.make()
.load(ClassLoader.getSystemClassLoader())
.getLoaded()
.newInstance();
System.out.println("moo:" + moo.Moo("tinysakura", 21));
System.out.println("moo1:" + moo.Moo1("tinysakura"));
输出结果

可以看到被匹配到的Moo和Moo1方法的实现都被覆盖为了DelegateMoo中的实现,ByteBuddy会根据最相似匹配原则去匹配委托类中可以覆盖原方法的实现,因此即使我将DelegateMoo中的Moo方法改名为Foo:
public class DelegateMoo {
public static String Moo1(String param1) {
return "my name is " + param1;
public static String Foo(String param1, Integer param2) {
return "my name is " + param1 + ",my age is " + param2;
得到的依然是一样的结果,因为bytebuddy这个方法除了方法名以外的签名都和委托的Moo方法一致。根据最似匹配原则会使用Foo方法的实现进行委托,而当匹配到的委托方法在被委托类中找不到任何可以匹配到的方法时,bytebuddy会抛出异常,比如我们在委托类中加入一个重载的不接受任何参数的Moo1方法:
public class Moo {
public String Moo1() {return null;}
public String Moo1(String param1) {return "parm1:" + param1;}
public String Moo(String param1, Integer param2) {return "param1:" + param1 + ",param2:"+ param2;}
此时再执行下面这段代码就会报错
Moo moo = new ByteBuddy()
.subclass(Moo.class)
.method(ElementMatchers.named("Moo").or(ElementMatchers.named("Moo1")))
.intercept(MethodDelegation.to(DelegateMoo.class))
.make()
.load(ClassLoader.getSystemClassLoader())
.getLoaded()
.newInstance();
java.lang.IllegalArgumentException: None of [public static java.lang.String com.tinysakura.bytebuddylearn.method.DelegateMoo.Moo1(java.lang.String), public static java.lang.String com.tinysakura.bytebuddylearn.method.DelegateMoo.Foo(java.lang.String,java.lang.Integer)] allows for delegation from public java.lang.String com.tinysakura.bytebuddylearn.method.Moo.Moo1()
因为在DelegateMoo中找不到任何可以匹配无参Moo1的实现,此时使用者需要被委托类的实现保证委托方法可以顺利找到被委托的方法。
上面的例子说明delegate的机制已经十分灵活了,但不止于此,比如我们想通过bytebuddy实现一个类似切面的功能,在匹配到的方法调用前加入执行时刻的日志,此时我们需要知道到委托方法原本的实现,该如何做?bytebuddy已经帮你想好了
@Super
我们定义一个新的被委托类DelegateMooWithSuper,在被委托方法Moo1的入参中加入了一个使用了@Super注解的委托类对象,此时这个被注解的入参在进行最似匹配时会被忽略,bytebuddy匹配到的依然是只有一个String入参的委托方法。这里也体现了bytebuddy结合注解达到高使用灵活度的代码风格。
public class DelegateMooWithSuper {
public static String Moo1(String param1, @Super Moo zuper) {
System.out.println("invoke time:" + System.currentTimeMillis());
return zuper.Moo1(param1);
@Test
public void testMethodDelegation() throws Exception {
Moo moo = new ByteBuddy()
.subclass(Moo.class)
.method(ElementMatchers.named("Moo").or(ElementMatchers.
named("Moo1")))
.intercept(MethodDelegation.to(DelegateMooWithSuper.class))
.make()
.load(ClassLoader.getSystemClassLoader())
.getLoaded()
.newInstance();
System.out.println("moo1:" + moo.Moo1("tinysakura"));

可以看到执行方法前打印了一行执行时间日志,我们这次没有在被委托方法中修改原方法的实现,所以依然使用的原方法的实现,通过这种机制我们可以在不破坏原方法逻辑的前提下,插入很多前置后置的逻辑,改变参数等,大大增加了字节码修改的灵活度。
@superCall
如果不需要修改入参,或者在被委托方法中调用超类其它方法,也可以使用@superCall注解
public static String Moo(String param1, Integer param2,
@SuperCall Callable<String> superCall) {
try {
String superCallResult = superCall.call();
return "wrapped by delegate:{" + superCallResult + "}";
} catch (Exception e) {
e.printStackTrace();
return null;
调用callable的call方法可以获取被委托方法的实现结果。
bytebuddy还提供了一些api对一些特殊方法,如构造方法,接口的默认方法做操作,这部分内容可以直接参考官方文档,最后,bytebuddy允许在interceptor中通过MethodCall去调用一个特定的方法,可以调用的包括构造方法,方法描述,可以通过这种机制更灵活的替换委托方法的实现。
上一节我们看到如何通过bytebuddy生成、实现方法,作为java类另一个非常重要的部分,bytebuddy也提供了大量操作属性的api,我们先从最简单的定义一个属性开始:
@Test
public void testField() throws Exception {
Moo moo = new ByteBuddy()
.subclass(Moo.class)
.defineField("name", String.class)
.make()
.load(ClassLoader.getSystemClassLoader())
.getLoaded()
.newInstance();
System.out.println(moo.getClass().getDeclaredField("name") != null);
看看反编译的结果,生成的子类中定义了一个string类型的name属性。
接下来我想给这个name赋值,那么我们需要一个getter和setter方法,可以通过FieldAccessor方便的为类的属性生成对应的getter和setter方法,为此我们需要准备一个对应属性的interceptor接口:
public interface NameInterceptor {
String getName();
void setName(String name);
然后通过FieldAccessor为我们新声明的name属性加入getter/setter方法:
@Test
public void testField() throws Exception {
new ByteBuddy()
.subclass(Moo.class)
.defineField("name", String.class, Modifier.PUBLIC)
.implement(NameInterceptor.class).intercept(FieldAccessor.ofBeanProperty())
.make().saveIn(new File("/Users/chenfeihao/Desktop/"));
下面是反编译的结果
public class Moo$ByteBuddy$FDuX1G5O extends Moo implements NameInterceptor {
public String name;
public String getName() {
return this.name;
public void setName(String var1) {
this.name = var1;
public Moo$ByteBuddy$FDuX1G5O() {
@FieldValue
之后我们就可以通过反射给name注入值了,在后续方法的代理实现中也可以通过@FieldValue注解使用这个原本的类中不存在的属性
我们通过DelegateWithField代理原始类的Moo1方法,同时打印出传参和name属性的值
public class DelegateWithField {
String name;
public static String Moo1(String param1, @FieldValue(value = "name") String name) throws Exception {return "parm1:" + param1 + ";name:" + name;}
@Test
public void testField() throws Exception {
Moo moo = new ByteBuddy()
.subclass(Moo.class)
.defineField("name", String.class, Modifier.PUBLIC)
.implement(NameInterceptor.class).intercept(FieldAccessor.ofBeanProperty())
.method(ElementMatchers.named("Moo1"))
.intercept(MethodDelegation.to(DelegateWithField.class))
.make().load(ClassLoader.getSystemClassLoader())
.getLoaded().newInstance();
moo.getClass().getMethod("setName", String.class).invoke(moo, "tinysakura"
);
System.out.println(moo.getClass().getMethod("Moo1", String.class).invoke(moo, "param1"));

照例看一波反编译的结果:
public class Moo$ByteBuddy$YNq6kjNU extends Moo implements NameInterceptor {
public String name;
public String Moo1(String var1) {
return DelegateWithField.Moo1(var1, this.name);
public String getName() {
return this.name;
public void setName(String var1) {
this.name = var1;
public Moo$ByteBuddy$YNq6kjNU() {
bytebuddy中使用了非常多的注解方便我们coding,当然也支持在字节码生成时加入注解,事实上注解在java代码中的使用非常普遍,bytebuddy提供的这些能力可以更方便的为增强的类提供能力
首先声明一个自定义的注解以及它的实现
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimeDefinition {
public class RuntimeDefinitionImpl implements RuntimeDefinition {
@Override
public Class<? extends Annotation> annotationType() {
return RuntimeDefinition.class;
然后我们可以在任何声明,匹配到的属性或方法上加上我们自定义的注解
@Test
public void testAnnotation() throws Exception {
new ByteBuddy()
.subclass(Moo.class)
.defineField("name", String.class, Modifier.PUBLIC)
.annotateField(new RuntimeDefinitionImpl())
.method(ElementMatchers.named("Moo1"))
.intercept(MethodDelegation.to(DelegateMoo.class))
.annotateMethod(new RuntimeDefinitionImpl())
.make().saveIn(new File("/Users/chenfeihao/Desktop/"));
反编译结果:
public class Moo$ByteBuddy$n5Zp31xO extends Moo {
@RuntimeDefinition
public String name;
@RuntimeDefinition
public String Moo1(String var1) {
return DelegateMoo.Moo1(var1);
public Moo$ByteBuddy$n5Zp31xO() {
之前介绍类的redefine时特别说明了我们只能redefine尚未被加载的类,然而有些需求必须得对那些”已加载的类"做增强,java自己本身提供了一套java agent的机制在虚拟机启动前对字节码做增强,而byte buddy很好的对其做了支持,阅读这一节需要有java agent方面的基础,可以通过阅读JAVA SE 6新特性:Instrumentation了解
我们还是通过一个示例进行学习,内容是对线程做增强,打印出所有线程执行前后的时间戳,并在任务执行前输出一句hello world。
首先定义我们的preMain程序
public class BytebuddyAgent {
public static void premain(String agentArgs, Instrumentation inst) throws Exception {
Class<?> bootStrapClass = Class.forName("com.tinysakura.bytebuddylearn.BootStrap", true, getClassLoader());
Method preMain = bootStrapClass.getMethod("preMain", String.class, Instrumentation.class);
preMain.invoke(null, agentArgs, inst);
public static ClassLoader getClassLoader(){
ClassLoader classLoader= Thread.currentThread().getContextClassLoader();
if (classLoader!=null){
return classLoader;
return ClassLoader.getSystemClassLoader();
agent启动的时候会执行preMain方法,我们在preMain方法中通过反射调用另外一个jar包中BootStrap类的同名preMain方法,注意这里将Instrumentation作为参数进行了传递
public static void preMain(String args, Instrumentation inst) {
AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, module) -> builder.visit(Advice.to(Interceptor.class).on(ElementMatchers.named("start")));
new AgentBuilder.Default()
.disableClassFormatChanges()
.enableBootstrapInjection(inst, new File("/Users/chenfeihao/Desktop/lib"))
.ignore(new AgentBuilder.RawMatcher.ForElementMatchers(nameStartsWith("net.bytebuddy.")))
.ignore(ElementMatchers.noneOf(Thread.class))
.with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(AgentBuilder.
TypeStrategy.Default.REDEFINE)
.type(ElementMatchers.is(Thread.class))
.transform(transformer)
.with(new MyAgentBuilderListener())
.installOn(inst);
bootStrap的preMain方法看上去有点复杂,我们逐步拆解
- 定义了一个transformer类型,通过访问者模式切入start方法
- 通过enableBootstrapInjection加入了新的classpath,确保我们的类文件能被找到
- 忽略一部分不想被误匹配的类
- 指定一系列redefine策略
- 通过type方法指定要匹配的类
- 通过transform方法加入1中定义的访问者,这样Thread类的start方法将被这个transformer访问到
- 增加一个redefine前的回调,完成一些前序工作
- 通过install Instrumentation赋予byte agent 在thread类加载前redefine的能力
先来看增强逻辑
public static class Interceptor {
@Advice.OnMethodEnter
public static void enter(@Advice.This Thread thread, @Advice.Origin Method origin) {
System.out.println("thread:" + thread.getName() + " enter thread timeMills:" + System.currentTimeMillis());
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
if (classLoader == null) {
try {
origin.invoke(null, thread);
} catch (Throwable e) {
e.printStackTrace();
return;
try {
Class reflectClass = Class.forName("com.tinysakura.bytebuddylearn.CustomThread", true, classLoader);
Method start = reflectClass.getMethod("start", Thread.class);
start.invoke(null, thread);
} catch (Throwable e) {
e.printStackTrace();
@Advice.OnMethodExit
public static void exit(@Advice.This Thread thread, @Advice.Origin Method origin) {
System.out.println("thread:" + thread.getName() + " exit thread timeMills:" + System.currentTimeMillis());
通过@Advice.This注解我们可以拿到实际执行方法的对象,通过@Advice.Origin注解可以拿到原方法,我们首先在进入和退出方法前打印出当前线程的线程名和当前时间戳,其中在enter方法的实现中通过反射获取到了我们”增强“过的start方法交给thread执行,下面我们看这个增强的start方法是怎么实现的
public class CustomThread {
public static void start(Thread thread) {
try {
System.out.println("welcome to my custom thread");
Field field = Thread.class.getDeclaredField("target");
field.setAccessible(true);
Object targetValue = field.get(thread);
if (targetValue instanceof Runnable) {
Runnable runnable = (Runnable) targetValue;
field.set(thread, new CustomeRunnable(runnable));
} catch (Throwable e) {
System.out.println(e);;
首先打印出"welcome to my custom thread"标志走进了增强后的start方法,随后通过反射获取到thread的target属性,最后使用我们自己的CustomeRunnable对Runnable类型的target属性再做一次封装:
public class CustomeRunnable implements Runnable {
Runnable delegate;
public CustomeRunnable(Runnable delegate) {
this.delegate = delegate;
@Override
public void run() {
System.out.println("welcome to my custom thread");
delegate.run();
为了演示,CustomeRunnable做的事情和增强的start方法差不多,也只是简单打印”welcome to my custom thread“
现在我们对增强的Thread的期望是在执行runnable的逻辑前先打印一下当前线程名和时间戳,随后打印两次welcome to my custom thread,接着执行增强前的逻辑,最后退出线程打印当前线程名和时间戳,我们启动一个线程观察增强结果:
public static void main(String[] args) {
new Thread(() -> {
System.out.println("hello bytebuddy agent");
for (int i=0; i < 10000L; i++) {
}, "tinysakura").start();
执行结果:

ByteBuddy官方文档ByteBuddy 介绍首先需要了解ByteBuddy是什么,ByteBuddy是一款java字节码增强框架,可以动态的生成java字节码文件,比起我们自己进行字节码文件的生成,它屏蔽了底层细节,提供一套统一易上手的Api,简化了字节码增强的学习难度。为什么需要字节码增强技术?ByteBuddy官方文档已经给出了答案The Java language comes with a comparatively strict type system. Java requires
Agentbuilder入门+API详解一、使用范式:创建一个agent二、 类结构2.1 嵌套的内部类2.2 方法
使用中发现源码中的高阶用法根本看不懂,狠下心来试着结合文档和源码看下Agentbuilder。
这篇文档会忽略java agent的基础知识,请自行百度。
对过于繁琐的代码,会说明意图,细节等。
首先会介绍用法的模板,涉及bytebuddy的各个类的作用,请参考bytebuddy简单入门
然后是按照类的结构来介绍Agentbuilder的功能。
希望我能写完吧,太难了。
一、使用范式
赠送jar包:
byte-
buddy-1.11.22.jar;
赠送原API文档:
byte-
buddy-1.11.22-javadoc.jar;
赠送源代码:
byte-
buddy-1.11.22-sources.jar;
赠送Maven依赖信息文件:
byte-
buddy-1.11.22.pom;
包含翻译后的API文档:
byte-
buddy-1.11.22-javadoc-API文档-中文(简体)版.zip;
Maven坐标:net.
bytebuddy:
byte-
buddy:1.11.22;
标签:
bytebuddy、
byte、
buddy、jar包、java、中文文档;
使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。
人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心
使用。
字节码增强技术-Byte Buddy
目录简介性能使用installHello World创建动态类加载类拦截方法通过匹配模式拦截方法委托参数绑定AgentElementMatcherTransformerEND
Byte Buddy是一个字节码生成和操作库,用于在Java应用程序运行时创建和修改Java类,而无需编译器的帮助。除了Java类库附带的代码生成实用程序外,Byte Buddy还允许创建任意类,并且不限于实现用于创建运行时代理的接口。此外,Byte Buddy提供了一种方便的API,可以使
基于
Bytebuddy字节码增强技术及Java Agent实现的无侵入式AOP框架
借鉴skywalking的设计原理开发,只保留最基本的match功能
代码简单,容易直接上手二次开发
dy-agent-core:核心功能代码,代码量不大,有兴趣可以瞧一瞧。
dy-agent-log4j:为了不与目标应用的日志框架产生冲突,自己实现的log4j。
如何添加新的拦截器
继承AbstractMethodInterceptor类,需要override两个方法: focusOn 以及 match。
focusOn
在focusOn中定义需要拦截的特定类,目前的matcher有NameMatch和MultiNameOrMatch。可自己实现更多Matcher。
match
对类中的相应方法进行拦截。也可直接返回true,表示拦截所有方法。
参考DemoInter
赠送jar包:byte-buddy-1.10.20.jar;
赠送原API文档:byte-buddy-1.10.20-javadoc.jar;
赠送源代码:byte-buddy-1.10.20-sources.jar;
赠送Maven依赖信息文件:byte-buddy-1.10.20.pom;
包含翻译后的API文档:byte-buddy-1.10.20-javadoc-API文档-中文(简体)版.zip;
Maven坐标:net.bytebuddy:byte-buddy:1.10.20;
标签:bytebuddy、byte、buddy、中文文档、jar包、java;
使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。
人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。
赠送jar包:byte-buddy-1.9.16.jar;
赠送原API文档:byte-buddy-1.9.16-javadoc.jar;
赠送源代码:byte-buddy-1.9.16-sources.jar;
赠送Maven依赖信息文件:byte-buddy-1.9.16.pom;
包含翻译后的API文档:byte-buddy-1.9.16-javadoc-API文档-中文(简体)-英语-对照版.zip;
Maven坐标:net.bytebuddy:byte-buddy:1.9.16;
标签:bytebuddy、byte、buddy、中英对照文档、jar包、java;
使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。
人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。
双语对照,边学技术、边学英语。
赠送jar包:
byte-
buddy-agent-1.10.22.jar;
赠送原API文档:
byte-
buddy-agent-1.10.22-javadoc.jar;
赠送源代码:
byte-
buddy-agent-1.10.22-sources.jar;
赠送Maven依赖信息文件:
byte-
buddy-agent-1.10.22.pom;
包含翻译后的API文档:
byte-
buddy-agent-1.10.22-javadoc-API文档-中文(简体)版.zip;
Maven坐标:net.
bytebuddy:
byte-
buddy-agent:1.10.22;
标签:
bytebuddy、
byte、
buddy、agent、中文文档、jar包、java;
使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。
人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心
使用。
当我们在使用 Maven 或 Gradle 构建项目时,我们可能会遇到以下错误信息:
unresolved dependency: 'net.bytebuddy:byte-buddy:jar:1.10.22'
这种错误通常发生在我们需要使用某些库或框架,而该库或框架依赖于 byte-buddy 1.10.22 这个版本。然而,在当前项目的仓库或镜像源中,无法找到 byte-buddy 1.10.22 这个版本的库文件,因此出现了 unresolved dependency 错误。此时,我们需要采取以下步骤来解决这个问题:
1. 检查网络连接
首先,我们应该检查我们的网络连接是否正常。如果网络连接出现问题,我们需要解决这个问题,以确保我们能够正常连接到仓库或镜像源。
2. 检查仓库或镜像源配置
我们需要检查我们的 Maven 或 Gradle 配置文件中是否正确配置了仓库或镜像源。在 Maven 的 settings.xml 或 Gradle 的 build.gradle 中,我们应该检查是否添加了正确的仓库或镜像源地址。如果没有添加或者添加的地址有误,我们需要修改配置文件,以保证能够正确地从仓库或镜像源中获取所需的库文件。
3. 更改版本依赖
如果仓库或镜像源中确实没有我们需要的 byte-buddy 1.10.22 这个版本的库文件,我们可以考虑更改我们的版本依赖。我们可以在 Maven 或 Gradle 的配置文件中修改依赖的版本号,以使用可用的版本。
总之,当出现 unresolved dependency 错误时,我们需要仔细检查我们的网络连接、仓库或镜像源配置,以及版本依赖等因素,以确定如何解决这个问题。