0%

java ByteBuddy

折腾了好久的javaagent不知为何就是不能在load类的时候进行其他类的执行,先放一放,了解一下ByteBuddy的使用

Byte Buddy 是一个代码生成和操作库,用于在Java应用程序运行时创建和修改Java类,而无需编译器的帮助。除了Java类库附带的代码生成实用程序外,Byte Buddy还允许创建任意类,并且不限于实现用于创建运行时代理的接口。此外,Byte Buddy提供了一种方便的API,可以使用Java代理或在构建过程中手动更改类。

比起javassist,asm,bcel起来都比较简单

个人理解就是为了解决Java静态语言的不方便之处,通过直接生成字节码然后使用JVM运行,和反射机制有点像,但是比反射机制效率更高,因为是生成字节码直接跑java虚拟机。

首先安装

maven的项目在pom.xml里面添加一个依赖

1
2
3
4
5
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.12.2</version>
</dependency>

看看用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

package com.iast;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;

/**
* MyAgent
*/
public class MyAgent {

public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Class<?> dynamicType = new ByteBuddy().subclass(Object.class).method(ElementMatchers.named("toString"))
.intercept(FixedValue.value("Hello World")).make().load(MyAgent.class.getClassLoader()).getLoaded();

Object instance = dynamicType.newInstance();
String toString = instance.toString();
System.out.println(toString);
System.out.println(instance.getClass().getCanonicalName());
}

}

完成动态构造一个类并且输出他的

  • subclass指定了新创建类的父类
  • method指定了Object的处理方法
  • intercept拦截了toString方法并返回指定的helllo world
  • 最后make产生字节码,由类加载器加载到虚拟机中

创建类

1
2
3
DynamicType.Unloaded<?> dynamicType = new ByteBuddy()
.subclass(Object.class)
.make();

创建一个类,还可以在make生成字节码之前.name("your_name")来进行命名

加载类

1
2
3
4
5
Class<?> type = new ByteBuddy()
.subclass(Object.class)
.make()
.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();

redefine和rebase

redefine修改之后就会把原来的类功能冲掉,变得不可用

rebase只是加了个前缀而已

重载类

相当于是和javaagent一样的功能,在类load的时候劫持并且运行我们自己的类

1
2
3
4
5
6
7
class Foo {
String m() { return "foo"; }
}

class Bar {
String m() { return "bar"; }
}
1
2
3
4
5
6
7
8
ByteBuddyAgent.install();
Foo foo = new Foo();
new ByteBuddy()
.redefine(Bar.class)
.name(Foo.class.getName())
.make()
.load(Foo.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
assertThat(foo.m(), is("bar"));

方法拦截

1
2
3
4
5
6
7
8
9
10
11
12
Foo dynamicFoo = new ByteBuddy()
.subclass(Foo.class)
// 匹配由Foo.class声明的方法
.method(isDeclaredBy(Foo.class)).intercept(FixedValue.value("One!"))
// 匹配名为foo的方法
.method(named("foo")).intercept(FixedValue.value("Two!"))
// 匹配名为foo,入参数量为1的方法
.method(named("foo").and(takesArguments(1))).intercept(FixedValue.value("Three!"))
.make()
.load(getClass().getClassLoader())
.getLoaded()
.newInstance();

参考

https://bytebuddy.net/#/tutorial