Agentbuilder入门+API详解
使用中发现源码中的高阶用法根本看不懂,狠下心来试着结合文档和源码看下Agentbuilder。
- 这篇文档会忽略java agent的基础知识,请自行百度。或者参考instrument
- 对过于繁琐的代码,会说明意图,细节等。
- 首先会介绍用法的模板,涉及
bytebuddy
的各个类的作用,请参考bytebuddy简单入门 - 然后是按照类的结构来介绍
Agentbuilder
的功能。
希望我能写完吧,太难了。
一、使用范式:创建一个agent
当一个应用变得庞大和模块化时,使用java agent
机制很合适。
定义
transformer
,每有一个类加载时,触发transformer
逻辑,对类进行匹配和修改。
bytebuddy提供了Agentbuilder
,封装了一系列API来新建一个 agent。
示例
假设我们定义了一个注解ToString
,我们匹配所有标注@ToString
的类,修改toString
方法,让其返回"transformed"
。
class ToStringAgent {
public static void premain(String arguments, Instrumentation instrumentation) {
new AgentBuilder.Default()
.type(isAnnotatedWith(ToString.class))
.transform(new AgentBuilder.Transformer() {
@Override
public DynamicType.Builder transform(DynamicType.Builder builder,
TypeDescription typeDescription,
ClassLoader classloader) {
return builder.method(named("toString"))
.intercept(FixedValue.value("transformed"));
}
}).installOn(instrumentation);
}
}
- type 接受 一个
ElementMatcher
匹配ToString注解
- transform 接受
AgentBuilder.Transformer
来描述修改逻辑
// 这里匹配类里面的`toString`方法,使用`intercept`修改返回值
public DynamicType.Builder transform(DynamicType.Builder builder,
TypeDescription typeDescription,
ClassLoader classloader) {
return builder.method(named("toString"))
.intercept(FixedValue.value("transformed"));
}
- installOn ,将修改,应用到
instrumentation
中。
二、AgentBuilder 类结构
2.1 嵌套的内部类
order | Modifier and Type | Interface | Description |
---|---|---|---|
0 | static interface | AgentBuilder.CircularityLock | 一个循环锁,阻止ClassFileLocator 被提前使用。发生在,一个 transformation 逻辑可能会导致另外的类被加载。如果不避免这样的循环依赖,就会抛出ClassCircularityError 错误,导致类加载失败 |
1 | static interface | AgentBuilder.ClassFileBufferStrategy | 关于class 定义的字节缓存buffer 如何被使用的策略 |
2 | static class | AgentBuilder.Default | AgentBuilder 的默认实现。默认情况下,byte buddy 无视任何被bootstrap loader 加载的类和合成类。1. Self-injection 和 rebase 模式开启 2. 为了避免class的格式发生改变,AgentBuilder.disableClassFormatChanges() 3. 所有的类型解析都是 PoolStrategy.Default#FAST ,忽略所有的debug信息 |
3 | static interface | AgentBuilder.DescriptionStrategy | 策略,描述当转化和定义一个类时,如何解决TypeDescription定义,这个解决指的是寻找+处理 |
4 | static interface | AgentBuilder.FallbackStrategy | 失败策略,允许万一失败时,可以再次进行transformation 或者redefine/retransformation 。如果这样做了,可能就是会使用typepool ,而不是一个已经加载的type description –相当于某个class的备份。如果class loader不能找到所有的依赖的类,会抛出异常。使用typepool ,由于时懒加载,所以会规避异常,直到被使用 |
5 | static interface | AgentBuilder.Identified | 用来描述AgentBuilder 处理哪几种mathcer,这个就是提供给mather一个标识,便于筛选 |
6 | static interface | AgentBuilder.Ignored | 允许声明,忽略那些具体的方法 |
7 | static interface | AgentBuilder.InitializationStrategy | 初始化策略,决定LoadedTypeInitializer 是如何加载辅助类。agentbuilder不能重用TypeResolutionStrategy 策略,是因为Javaagents不能获取到一个被transformation过,已经加载的类。所以所以不同的初始化加载策略更有效 |
8 | static interface | AgentBuilder.InjectionStrategy | 将辅助类注入classloader 的策略 |
9 | static interface | AgentBuilder.InstallationListener | 监听器,安装和重置class file transformer 事件会唤醒这个监听器 |
10 | static class | AgentBuilder.LambdaInstrumentationStrategy | lambda风格的语法特性开启 |
11 | static interface | AgentBuilder.Listener | 一个an instrumentation 运行时会触发一堆事件,这个监听器是用来接受这些事件的 |
12 | static interface | AgentBuilder.LocationStrategy | 修改一个类时,描述如何去创建ClassFileLocator的策略 |
13 | static interface | AgentBuilder.Matchable<T extends AgentBuilder.Matchable> | 继承了mathcer的一个抽象实现,链式的结构 |
14 | static interface | AgentBuilder.PoolStrategy | 描述AgentBuilder 如何寻找和加载类TypeDescription 的策略 |
15 | static interface | AgentBuilder.RawMatcher | 一个matcher ,用来匹配类并决定AgentBuilder.Transformer 在ClassFileTransformer 运行期间是否被使用 |
16 | static interface | AgentBuilder.RedefinitionListenable | 允许在redefine 的过程中注册一堆监听器 |
17 | static class | AgentBuilder.RedefinitionStrategy | redefinition 策略,描述agent 如何控制已经被agent 加载到内存里面的类 |
18 | static interface | AgentBuilder.Transformer | 应用DynamicType(定义的类修改) ,将DynamicType ,对接到ClassFileTransformer |
19 | static interface | AgentBuilder.TransformerDecorator | AgentBuilder.Transformer 的装饰器 |
20 | static interface | AgentBuilder.TypeStrategy | 描述创建一个要被修改的类型,如何创建的方式 |
2.2 方法
相当于API的翻译,会有简单解释,觉得不准请看上面的API文档。
- 加载过 往往意味值loaded,就是指classloader已经加载过目标类。bytebuddy通常就是感知类的加载,并且返回一个加工过的类。如此完成字节码修改的作用。
order | return type | method | 描述 |
---|---|---|---|
0 | AgentBuilder | with(ByteBuddy byteBuddy) | ByteBuddy 是用来创建DynamicType 的。这里就是接受并生成一个AgentBuilder |
1 | AgentBuilder | with(Listener listener) | 注册监听器,创建agent的时候,会唤醒这个Listener。注意的是可以注册多个,如果早已经注册,也会重复唤醒,不会只唤醒一个 |
2 | AgentBuilder | with(CircularityLock circularityLock) | 一个循环锁,被用于被执行的代码会加载新的类时,会获取这个锁。当锁被获取时,任何classfiletransformer都不能transforming 任何类,默认,所有被创建的agent都使用一个CircularityLock ,避免造成一个ClassCircularityError 。就是避免被执行的代码加载新类,同时其他agent也在转化这个类,造成一个循环依赖的一个锁 |
3 | AgentBuilder | with(PoolStrategy poolStrategy) | 加载的类时的策略 |
4 | AgentBuilder | with(Locati |