bytebuddy实现原理分析 &源码分析(一)
bytebuddy实现原理分析 &源码分析 (二)
bytebuddy实现原理分析 &源码分析 (三)
bytebuddy实现原理分析 &源码分析 (四)
byte buddy中对字节码的操作,直接调用了asm的api。
比如 FixedValue
是修改方法时定义返回的值,比如toString()=="hello"
改成toString()=="changed"
,这个意味着要把.class
字节码中的变量"hello"
替换成"changed"
。
如果是修改方法的定义,比如methodCall
。就要修改字节码指令,本地变量栈和操作数栈。
方法的编译成字节码后就是,字节码指令和本地变量栈和操作数栈。完成方法的逻辑,就是顺序的执行字节码指令操作栈帧。不了解的同学可以先去补充字节码的知识。
asm 本身包含了字节码的各类模型,并且提供API来修改。但是这是low-level
级别的。比如我要定一个加法运算,就要使用API生成多个字节码指令
。
byte buddy 就是对这样大量重复的操作做了上层的封装。
源码关于字节码的实现可以大概分为两部分: 字节码封装
,操作字节码的API
方法名 | 描述 | |
---|---|---|
boolean isValid() |
判断生成的操作数栈是否合法 | |
Size apply (MethodVisitor methodVisitor, Implementation.Context implementationContext); |
methodVisitor 是asm的接口用来生成字节码,Implementation.Context 中定义了字节码。这个函数的作用是应用定义的字节码 |
方法名 | 类型 | 描述 |
---|---|---|
Illegal | 实现 | 非法的实现 |
Trivial | 实现 | 合法的实现,但是不会对原先的字节码有任何更改 |
Compound | 实现 | 可以对多个方法的栈大小进行合并运算 |
类名 | 类型 | 描述 |
---|---|---|
Addition | 实现 | 操作数栈的数字想加 |
Duplication | 实现 | 复制栈定的数 |
Multiplication | 实现 | 操作数栈的数字相乘 |
Removal | 实现 | 移出操作数栈的值 |
TypeCreation | 实现 | 当一个构造器方法被调用时,创建一个未定义的类型 |
Throw | 实现 | 必须位于栈顶,当StackManipulation 被调用时抛出一个异常java.lang.Throwable |
看一个具体的例子Duplication
, 其他的方法也类似: 封装字节码指令;重写apply
。
示例
复制一个在操作数栈,复制一个double 类型的数。和普通int不一样,long和double需要占1到2卡槽(2* 4 byte)。 所以这个枚举变量DOUBLE
,和字节码指令int DUP2 = 92;
关联。
封装字节码指令
/**
* A duplication of a double-sized stack value.
*/
DOUBLE(StackSize.DOUBLE, Opcodes.DUP2) {
@Override
public StackManipulation flipOver(TypeDefinition typeDefinition) {
switch (typeDefinition.getStackSize()) {
case SINGLE:
return WithFlip.DOUBLE_SINGLE;
case DOUBLE:
return WithFlip.DOUBLE_DOUBLE;
default:
throw new IllegalArgumentException("Cannot flip: " + typeDefinition);
}
}
};
新建一个Duplication
,有两个方法
/** 直接输入栈大小,和字节码指令
*/
Duplication(StackSize stackSize, int opcode) {
size = stackSize.toIncreasingSize();
this.opcode = opcode;
}
/**
* 根据类型的定义,获取栈大小
*/
public static Duplication of(TypeDefinition typeDefinition) {
switch (typeDefinition.getStackSize()) {
case SINGLE:
return SINGLE;
case DOUBLE:
return DOUBLE;
case ZERO:
return ZERO;
default:
throw new AssertionError("Unexpected type: " + typeDefinition);
}
}
实现apply
看看apply的实现,核心是methodVisitor.visitInsn(opcode);
这就是asm原生的方法,作用实在字节码码中生成这么一个指令。
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
methodVisitor.visitInsn(opcode);
return size;
}
这个包封装了jvm规范中的两个基本类型:基本类型,引用类型
。负责控制不同类型之间的转换
一个Assigner负责把一个类型A
转换成类型B
。比如一个Assigner
可以负责类型转化:装箱,把一个基本类型(int),转化为包转类(Integer);拆箱,正好相反。
指示,累型的转化是静态的还是动态的。静态的意思是,调用前已经确定了。动态类型时,生成是才确定。默认值是动态生成。
/**
* 静态生成
*/
STATIC(false),
/**
* 动态生成
*/
DYNAMIC(true);
assign
(TypeDescription.Generic source, TypeDescription.Generic target, Typing typing);Assigner的实现。
enum EqualTypesOnly implements Assigner {
/**
* An type assigner that only considers equal generic types to be assignable.
*/
GENERIC {
/** {@inheritDoc} */
public StackManipulation assign(TypeDescription.Generic source, TypeDescription.Generic target, Typing typing) {
return source.equals(target)
? StackManipulation.Trivial.INSTANCE
: StackManipulation.Illegal.INSTANCE;
}
},
/**
* A type assigner that considers two generic types to be equal if their erasure is equal.
*/
ERASURE {
/** {@inheritDoc} */
public StackManipulation assign(TypeDescription.Generic source, TypeDescription.Generic target, Typing typing) {
return source.asErasure().equals(target.asErasure())
? StackManipulation.Trivial.INSTANCE
: StackManipulation.Illegal.INSTANCE;
}
};
}
GENERIC
转换前直接判断类型是否想等
ERASURE
擦除类型后判断
代表了不能转化的类型。调用assign
直接抛出异常。
封装了 基本类型和引用类型各自的转化方法。可以进入源码细看,不什么
封装了数组与集合的字节码。
这个一个示例,可以看见它的定义把不同类型和对应的字节码指令进行了绑定
比如BYTE(Opcodes.BALOAD, Opcodes.BASTORE, StackSize.SINGLE)
:Opcodes.BALOAD
代表byte值的加载;Opcodes.BASTORE
代表了byte值放入操作数栈的指令;StackSize.SINGLE
代表了大小,single是4byte
public enum ArrayAccess {
/**
* Access for a {@code byte}- or {@code boolean}-typed array.
*/
BYTE(Opcodes.BALOAD, Opcodes.BASTORE, StackSize.SINGLE),
/**
* Access for a {@code short}-typed array.
*/
SHORT(Opcodes.SALOAD, Opcodes.SASTORE, StackSize.SINGLE),
/**
* Access for a {@code char}-typed array.
*/
CHARACTER(Opcodes.CALOAD, Opcodes.CASTORE, StackSize.SINGLE),
/**
* Access for a {@code int}-typed array.
*/
INTEGER(Opcodes.IALOAD, Opcodes.IASTORE, StackSize.SINGLE),
/**
* Access for a {@code long}-typed array.
*/
LONG(Opcodes.LALOAD, Opcodes.LASTORE, StackSize.DOUBLE),
/**
* Access for a {@code float}-typed array.
*/
FLOAT(Opcodes.FALOAD, Opcodes.FASTORE, StackSize.SINGLE),
/**
* Access for a {@code double}-typed array.
*/
DOUBLE(Opcodes.DALOAD, Opcodes.DASTORE, StackSize.DOUBLE),
/**
* Access for a reference-typed array.
*/
REFERENCE(Opcodes.AALOAD, Opcodes.AASTORE, StackSize.SINGLE);
类似,但是更复杂一点。代表了field和method。
注意Opcodes.INVOKEVIRTUAL
,Opcodes.INVOKESTATIC
是几种字节码指令,用来在方法中调用其他方法的的指针。
代表了字节码的属性。
比如method
的字节码结构,它的属性就是一个表。
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
// 这里
u2 attributes_count;
attribute_info attributes[attributes_count];
// 结束
}
分析其中一个类MethodAttributeAppender
,
核心的接口
apply
(MethodVisitor methodVisitor, MethodDescription methodDescription, AnnotationValueFilter annotationValueFilter);methodVisitor
就是asm 用来生成类的接口;methodDescription
方法的定义;annotationValueFilter
就是注解的过滤器,可以过滤掉methodDescription
不想要的注解。
辅助类型,什么辅助类型:修改原始类的字节码,织入一些方法为其他的织入方法提供帮助,注意一定要实现有意义的equals()
和hashCode()
,避免相同的目标类重复放入。
举个例
class A
的方法String hello()
,要替换成class B
的方法String hello()
。简单的实现是: 修改
A
的实现字节码,实现中调用B的hello
方法。
A.hello()–>B.hello()复杂的实现:定义一个
helloProxy()
方法,修改A
的实现字节码,实现中调的helloProxy()
方法。然后helloProxy()
中调用B的实现。
A.hello–> Proxy.helloProxy()–>B.helloProxy()。复杂实现的好处是,如果要换成
class C
的方法String hello()
,那么无需再特定的实现,和Proxy.helloProxy()
交互就可以。所以看出织入
A
的代码并不是目的代码,而是一个辅助的工具类Proxy
。这就是辅助类的作用。
make
(String auxiliaryTypeName, ClassFileVersion classFileVersion, MethodAccessorFactory methodAccessorFactory);SignatureRelevant
注解,标注辅助类型是否参与类型签名的生成,避免生成前面时报NoClassDefFoundError
错误AuxiliaryType的实现,就是前面描述的方法代理
make
方法的实现:生成了一个代理类
public DynamicType make(String auxiliaryTypeName,
ClassFileVersion classFileVersion,
MethodAccessorFactory methodAccessorFactory) {
MethodDescription accessorMethod = methodAccessorFactory.registerAccessorFor(specialMethodInvocation, MethodAccessorFactory.AccessType.DEFAULT);
LinkedHashMap<String, TypeDescription> parameterFields = extractFields(accessorMethod);
DynamicType.Builder<?> builder = new ByteBuddy(classFileVersion)
.with(TypeValidation.DISABLED)
.with(PrecomputedMethodGraph.INSTANCE)
.subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
.name(auxiliaryTypeName)
.modifiers(DEFAULT_TYPE_MODIFIER)
.implement(Runnable.class, Callable.class).intercept(new MethodCall(accessorMethod, assigner))
.implement(serializableProxy ? new Class<?>[]{Serializable.class} : new Class<?>[0])
.defineConstructor().withParameters(parameterFields.values())
.intercept(ConstructorCall.INSTANCE);
for (Map.Entry<String, TypeDescription> field : parameterFields.entrySet()) {
builder = builder.defineField(field.getKey(), field.getValue(), Visibility.PRIVATE);
}
return builder.make();
}
其余的类不在详细的说了。
Implementation 负责把动态创建的类Dynamic.type
,转化为字节码。一个Implementation
的使用可以按如下两步。
Implementation
能够去准备一个instrumentedType
,通过添加field
和辅助类(以便于添加方法的的 Implementation
。此外,LoadedTypeInitializer
和 类型初始化的字节码,可以被注册为instrumentedType
。Implementation
提供一个code appender
,可以被用来做方法的实现的委托调用。code appender
也负责去提供在 step 1中添加的类型。同时任何的Implementation
应该提供有意义的equals
和hashCode
,避免重复生成。
核心的方法
appender
(Target implementationTarget);Target是当前的implementation
,这个的意思是基于当前的implementation
创建一个ByteCodeAppender
。
链式的implementation,
interface Composable extends Implementation {
/**
* Appends the supplied implementation to this implementation.
*
* @param implementation The subsequent implementation.
* @return An implementation that combines this implementation with the provided one.
*/
Implementation andThen(Implementation implementation);
/**
* Appends the supplied composable implementation to this implementation.
*
* @param implementation The subsequent composable implementation.
* @return A composable implementation that combines this implementation with the provided one.
*/
Composable andThen(Composable implementation);
}
特殊的方法调用。比如class A
和 class B extends A
。现在只有在B内才能调用super
方法。但是可以通过修改字节码的方式,委托调用super
方法。这就是特殊的方式调用,但不限于上面描述的场景。
interface SpecialMethodInvocation extends StackManipulation {
一个 implementation 的目标。目标必须是不可变得,并且查询时返回固定结果,不能有随机的结果。Target提供生成implementation
的一切信息。
十分重要的一个内部类。包含了一个 Implenentation有关的上下文信息
核心方法
auxiliaryTypes
保存了所有辅助类的信息 public TypeDescription register(AuxiliaryType auxiliaryType) {
DynamicType dynamicType = auxiliaryTypes.get(auxiliaryType);
if (dynamicType == null) {
dynamicType = auxiliaryType.make(auxiliaryTypeNamingStrategy.name(instrumentedType), auxiliaryClassFileVersion, this);
auxiliaryTypes.put(auxiliaryType, dynamicType);
}
return dynamicType.getTypeDescription();
}
cache
(StackManipulation fieldValue, TypeDescription fieldType);drain
方法public void drain(TypeInitializer.Drain drain,
ClassVisitor classVisitor,
AnnotationValueFilter.Factory annotationValueFilterFactory) {
fieldCacheCanAppendEntries = false;
TypeInitializer typeInitializer = this.typeInitializer;
for (Map.Entry<FieldCacheEntry, FieldDescription.InDefinedShape> entry : registeredFieldCacheEntries.entrySet()) {
FieldVisitor fieldVisitor = classVisitor.visitField(entry.getValue().getModifiers(),
entry.getValue().getInternalName(),
entry.getValue().getDescriptor(),
entry.getValue().getGenericSignature(),
FieldDescription.NO_DEFAULT_VALUE);
if (fieldVisitor != null) {
fieldVisitor.visitEnd();
typeInitializer = typeInitializer.expandWith(entry.getKey().storeIn(entry.getValue()));
}
}
drain.apply(classVisitor, typeInitializer, this);
for (TypeWriter.MethodPool.Record record : registeredAccessorMethods.values()) {
record.apply(classVisitor, this, annotationValueFilterFactory);
}
for (TypeWriter.MethodPool.Record record : registeredGetters.values()) {
record.apply(classVisitor, this, annotationValueFilterFactory);
}
for (TypeWriter.MethodPool.Record record : registeredSetters.values()) {
record.apply(classVisitor, this, annotationValueFilterFactory);
}
}
这个类就是使用asm的接口classVisitor.visitField
,生成类文件。
一组implemention
Implementation的一个实现,用来修改原始方法,让其返回一个我们定义的值。
FixedValue有两个field
assigner
和typing
。之前介绍过assigner
是负责类型转换的,typing
是决定是否强制装换的。核心的方法
apply
(MethodVisitor methodVisitor,下面是类的实现调用visit
方法,生成成员。最后返回的StackSize
,相当于VisitEnd
的作用
protected ByteCodeAppender.Size apply(MethodVisitor methodVisitor,
Context implementationContext,
MethodDescription instrumentedMethod,
TypeDescription.Generic fixedValueType,
StackManipulation valueLoadingInstruction) {
StackManipulation assignment = assigner.assign(fixedValueType, instrumentedMethod.getReturnType(), typing);
if (!assignment.isValid()) {
throw new IllegalArgumentException("Cannot return value of type " + fixedValueType + " for " + instrumentedMethod);
}
StackManipulation.Size stackSize = new StackManipulation.Compound(
valueLoadingInstruction,
assignment,
MethodReturn.of(instrumentedMethod.getReturnType())
).apply(methodVisitor, implementationContext);
return new ByteCodeAppender.Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
}
这个是最外层的类,使用者,直接用这个类来生成想要的类。官方文档已经有了充分的介绍。这里罗列下方法。看着方法就可以猜到用法。
用来生成java 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);
}
}
ElementMatcher
匹配 ToString注解
AgentBuilder.Transformer
来描述修改逻辑// 这里匹配类里面的`toString`方法,使用`intercept`修改返回值
public DynamicType.Builder transform(DynamicType.Builder builder,
TypeDescription typeDescription,
ClassLoader classloader) {
return builder.method(named("toString"))
.intercept(FixedValue.value("transformed"));
}
instrumentation
中。bytebuddy的API
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 |
继承了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 | 描述创建一个要被修改的类型,如何创建的方式 |
相当于API的翻译,会有简单解释,觉得不准请看上面的API文档。
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(LocationStrategy locationStrategy) | 寻找类的位置,利用 class name 加载类二进制数据的策略 |
5 | AgentBuilder | with(TypeStrategy typeStrategy) | type 应该如何被transformed,eg, rebased或者redefined |
6 | AgentBuilder | with(InitializationStrategy initializationStrategy) | 生成一个类时的初始化策略,一个初始化策略是在类被加载后,启动时生效。初始化行为,必须在transformation 行为之后,因为java agent 是在加载一个类之前生效。默认,被注入到对象的初始化逻辑,需要查询一个全局对象来找到所有需要被注入到目标类型的对象 |
7 | RedefinitionListenable.WithoutBatchStrategy | with(RedefinitionStrategy redefinitionStrategy) | 明确早已经被修改过且加被加载过类的优先级,目的是来安转transformer会用到。注意定一个redefinition strategy 会重置之前的一些列的策略。重要的是,绝大多数JVM不支持被加载过的类,类结构被重新修改,因此默认打开AgentBuilder#disableClassFormatChanges() |
8 | AgentBuilder | with(LambdaInstrumentationStrategy lambdaInstrumentationStrategy) | Lambda表达式特性,忽虑掉 |
9 | AgentBuilder | with(DescriptionStrategy descriptionStrategy)) | 定义被加载类的,解决(resolving)策略。 |
10 | AgentBuilder | (FallbackStrategy fallbackStrategy) | 失败策略,比如转换失败时,允许重试一次 |
11 | AgentBuilder | with(ClassFileBufferStrategy classFileBufferStrategy) | 类定义的buffer如何被使用 |
12 | AgentBuilder | with(InstallationListener installationListener) | agent安转时,会有安装事件,这个时间会唤醒这个监听器。这个监听器,只有在classfiletransformer 安装时会被触发。classfiletransformer 的安装就是agent builder 通过创建的ResettableClassFileTransformer 执行installation methods 和uninstalled |
13 | AgentBuilder | with(InjectionStrategy injectionStrategy) | 辅助类注入到classloader的策略 |
14 | AgentBuilder | enableNativeMethodPrefix(String prefix) | 给被修改的方法,添加本地方法前缀。注意这个前缀也能给非本地方法用。当Instrumentation 安装agent 是,也能用这个前缀来注册 |
15 | AgentBuilder | disableNativeMethodPrefix() | 关闭一个本地方法的前缀 |
16 | AgentBuilder | disableClassFormatChanges() | 禁止所有对class file 的改变,当时用这个策略时,就不存在使用rebase 的可能—被修改的方法会完全被替代,而不是被重名。可去搜索rebase和redefine的区别。还有就是在生成类型是,加载初始化动作。这个行为和设置InitializationStrategy.NoOp 和TypeStrategy.Default#REDEFINE_FROZEN 是等价的。也等同于配置ByteBuddy 为Implementation.Context.Disabled 。使用这个策略是让bute buddy 穿件一个 冻结的 instrumented 类型和排除调所有的配置行为 |
17 | AgentBuilder | assureReadEdgeTo(Instrumentation instrumentation, Class>… type) | module ,jdk9的概念,不用管 |
18 | AgentBuilder | assureReadEdgeTo(Instrumentation instrumentation, JavaModule… module) | |
19 | AgentBuilder | assureReadEdgeTo(Instrumentation instrumentation, Collection extends JavaModule> modules) | |
20 | AgentBuilder | assureReadEdgeFromAndTo(Instrumentation instrumentation, Class>… type) | |
21 | AgentBuilder | assureReadEdgeFromAndTo(Instrumentation instrumentation, JavaModule… module) | |
22 | AgentBuilder | assureReadEdgeFromAndTo(Instrumentation instrumentation, Collection extends JavaModule> modules) | 都是moudle权限控制相关的 |
23 | Identified.Narrowabler | type(ElementMatcher super TypeDescription> typeMatcher) | 每一个类被加载时会触发一个事件,被agent感知到。这个方法就是用来配一个被加载的类型,目的是应用AgentBuilder.Transformer 的修改,在这个类型被加载之前。如果有几个matcher都命中一个类型,那么最后一个生效。如果这个matcher是链式的,即还有下一个matcher,那么matcher 执行的顺序就是注册的顺序,后面matcher对应的transformations 会覆盖前面的。如果不想被覆盖可以注册为terminal 最后一个Identified.Extendable#asTerminalTransformation() ,这样就不有有下一个matcher被使用。注意: AgentBuilder#ignore(ElementMatcher) 会在mather之前被应用,来排除忽略的类。 |
24 | Identified.Narrowabler | type(ElementMatcher super TypeDescription> typeMatcher, ElementMatcher super ClassLoader> classLoaderMatcher) | 唯一的不同是添加了classLoaderMatcher 参数,这个classLoader 被用来加载目标类的loader |
25 | Identified.Narrowabler | Identified.Narrowable type(ElementMatcher super TypeDescription> typeMatcher,ElementMatcher super ClassLoader> classLoaderMatcher,ElementMatcher super JavaModule> moduleMatcher); | moduleMatcher jdk9的语法,只是多了层判断 |
26 | Identified.Narrowabler | type(RawMatcher matcher); | RawMatcher 后面详细分析,文档只说用来决定Transformer 是否被使用 |
27 | Ignored | ignore(ElementMatcher super TypeDescription> typeMatcher); | 无视方法的matcher |
28 | Ignored | ignore(ElementMatcher super TypeDescription> typeMatcher, ElementMatcher super ClassLoader> classLoaderMatcher) | |
29 | Ignored | ignore(RawMatcher rawMatcher) | |
30 | ClassFileTransformer | makeRaw() | ClassFileTransformer 是jdk instrument包的类。这个方法是创建一个原生的这样的类。当时用原生的ClassFileTransformer ,InstallationListener 回调就不会生效,而且RedefinitionStrategy 的策略也不会应用到当前加载的类中 |
31 | ResettableClassFileTransformer | installOn(Instrumentation instrumentation) | 单纯使用instrument 时,往往是在premain 或者agentmain 函数里,执行instrumentation.addTransformer(ClassFileTransformer) ,installOn 就是将创建一个ResettableClassFileTransformer 添加进instrumentation 。如果retransformation 是打开的,那么retransformed时让ClassFileTransformer 再次生效。意思是ClassFileTransformer 是链式的,每个ClassFileTransformer 被添加的时候有个canRetransformed 属性。retransformed ,就是修改已经加载内存里面的类,执行时会再次触发ClassFileTransformer ,这时只会触发canRetransformed 为true的ClassFileTransformer |
32 | ResettableClassFileTransformer | ResettableClassFileTransformer installOn(Instrumentation instrumentation, TransformerDecorator transformerDecorator); | installOn时接受额外的Transformer |
33 | ResettableClassFileTransformer | installOnByteBuddyAgent() | ByteBuddyAgent 是bytebutty对jdk.attach 和agentmain 的一个封装。根据目标jvm实例的pid,在运行中attach上去,发出加载agent.jar 包命令 |
34 | ResettableClassFileTransformer | installOnByteBuddyAgent(TransformerDecorator transformerDecorator); |
不同于Bytebuddy
类,这个类还有 listener
机制,为什么呢。
java的instrument包
加载javaagent包,其实是有时间点的,可以人为的在不同时刻使用监听器机制做些什么。
这个监听器发生在 instrumentation api
作用的时候,也就是开始加载java agent
的时候。
onDiscovery
(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded);transformer
开始加载前。onTransformation
(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType);onIgnored
(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded);onError
(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable);onComplete
(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded);这个方法在哪里被调用了,比如
transform这个方式就是继承ClassFileTransformer
类。ClassFileTransformer
是instrument包中类,当进行类的修改时会执行这个transform()
方法。
可以看到这个类内,一开始就调用了onDiscovery
。
其他的方法也类似。
这个会在那哪里调用呢,比如 agent的installOn
,最终会调到一个doInstall
的方法。
instrumentedType.getDeclaredFields().filter(named(ENUM_VALUES)).getOnly()
getDeclaredFields
找到一个复合match条件的方法