bytebuddy实现原理分析 &源码分析(一)
bytebuddy实现原理分析 &源码分析 (二)
bytebuddy实现原理分析 &源码分析 (三)
bytebuddy实现原理分析 &源码分析 (四)
增强,切面编程。
asm入门
asm手册
大致了解了asm,就可以知道Visitor
是asm进行读写解析.class
字节码文件的核心api。切面的原理就是匹配所有的方法,在方法前后加入额外的方法,AdviceAdapter
类提供了onMethodEnter
和onMethodExit
两个方法,实现就可以做到简单的切面。
bytebuddy 的增强就是基于 asm的core API然后提供了一些列注解,方法用户使用。
就是对visitor的一个封装。
visitor
以分为 ClassReader
和ClassWriter
。
核心方法
flags|foo
wrap
(TypeDescription
instrumentedType,ClassVisitor
classVisitor,Implementation.Context
implementationContext,TypePool
typePool,FieldList
fields,MethodList>
methods,int
writerFlags,int
readerFlags);DynamicType
定义的类,使用ClassVisitor
生成字节码。这个类的作用是报存 matcher和visitor的对应关系。使用matcher命中被修改的区域,visitor来应用修改逻辑。
protected static class Entry implements ElementMatcher<FieldDescription.InDefinedShape>, FieldVisitorWrapper {
/**
* The matcher to identify fields to be wrapped.
*/
private final ElementMatcher<? super FieldDescription.InDefinedShape> matcher;
/**
* The field visitor wrapper to be applied if the given matcher is matched.
*/
private final List<? extends FieldVisitorWrapper> fieldVisitorWrappers;
/**
* Creates a new entry.
*
* @param matcher The matcher to identify fields to be wrapped.
* @param fieldVisitorWrappers The field visitor wrapper to be applied if the given matcher is matched.
*/
protected Entry(ElementMatcher<? super FieldDescription.InDefinedShape> matcher, List<? extends FieldVisitorWrapper> fieldVisitorWrappers) {
this.matcher = matcher;
this.fieldVisitorWrappers = fieldVisitorWrappers;
}
/**
* {@inheritDoc}
*/
public boolean matches(FieldDescription.InDefinedShape target) {
return target != null && matcher.matches(target);
}
/**
* {@inheritDoc}
*/
public FieldVisitor wrap(TypeDescription instrumentedType, FieldDescription.InDefinedShape fieldDescription, FieldVisitor fieldVisitor) {
for (FieldVisitorWrapper fieldVisitorWrapper : fieldVisitorWrappers) {
fieldVisitor = fieldVisitorWrapper.wrap(instrumentedType, fieldDescription, fieldVisitor);
}
return fieldVisitor;
}
}
继承ClassVisitor方法
protected class DispatchingVisitor extends ClassVisitor {
/**
* The instrumented type.
*/
private final TypeDescription instrumentedType;
/**
* A mapping of fields by their name and descriptor key-combination.
*/
private final Map<String, FieldDescription.InDefinedShape> fields;
/**
* Creates a new dispatching visitor.
*/
protected DispatchingVisitor(ClassVisitor classVisitor, TypeDescription instrumentedType, Map<String, FieldDescription.InDefinedShape> fields) {
super(OpenedClassReader.ASM_API, classVisitor);
this.instrumentedType = instrumentedType;
this.fields = fields;
}
@Override
public FieldVisitor visitField(int modifiers, String internalName, String descriptor, String signature, Object defaultValue) {
FieldVisitor fieldVisitor = super.visitField(modifiers, internalName, descriptor, signature, defaultValue);
FieldDescription.InDefinedShape fieldDescription = fields.get(internalName + descriptor);
if (fieldVisitor != null && fieldDescription != null) {
for (Entry entry : entries) {
if (entry.matches(fieldDescription)) {
fieldVisitor = entry.wrap(instrumentedType, fieldDescription, fieldVisitor);
}
}
}
return fieldVisitor;
}
}
visitField
,使用entry.wrap
最终将fieldVisitor
委托给FieldVisitorWrapper
接口。所以FieldVisitorWrapper
的子类只要实现FieldVisitorWrapper
的wrap
方法,在其中定义具体的修改逻辑。 public interface FieldVisitorWrapper {
/**
* Wraps a field visitor.
*
* @param instrumentedType The instrumented type.
* @param fieldDescription The field that is currently being defined.
* @param fieldVisitor The original field visitor that defines the given field.
* @return The wrapped field visitor.
*/
FieldVisitor wrap(TypeDescription instrumentedType, FieldDescription.InDefinedShape fieldDescription, FieldVisitor fieldVisitor);
}
也是封装了上面提到的类
class ForDeclaredFields extends AbstractBase {
/**
* The list of entries that describe matched fields in their application order.
*/
private final List<Entry> entries;
/**
* Creates a new visitor wrapper for declared fields.
*/
public ForDeclaredFields() {
this(Collections.<Entry>emptyList());
}
/**
* Creates a new visitor wrapper for declared fields.
*
* @param entries The list of entries that describe matched fields in their application order.
*/
protected ForDeclaredFields(List<Entry> entries) {
this.entries = entries;
}
/**
* Defines a new field visitor wrapper to be applied if the given field matcher is matched. Previously defined
* entries are applied before the given matcher is applied.
*
* @param matcher The matcher to identify fields to be wrapped.
* @param fieldVisitorWrapper The field visitor wrapper to be applied if the given matcher is matched.
* @return A new ASM visitor wrapper that applied the given field visitor wrapper if the supplied matcher is matched.
*/
public ForDeclaredFields field(ElementMatcher<? super FieldDescription.InDefinedShape> matcher, FieldVisitorWrapper... fieldVisitorWrapper) {
return field(matcher, Arrays.asList(fieldVisitorWrapper));
}
/**
* Defines a new field visitor wrapper to be applied if the given field matcher is matched. Previously defined
* entries are applied before the given matcher is applied.
*
* @param matcher The matcher to identify fields to be wrapped.
* @param fieldVisitorWrappers The field visitor wrapper to be applied if the given matcher is matched.
* @return A new ASM visitor wrapper that applied the given field visitor wrapper if the supplied matcher is matched.
*/
public ForDeclaredFields field(ElementMatcher<? super FieldDescription.InDefinedShape> matcher, List<? extends FieldVisitorWrapper> fieldVisitorWrappers) {
return new ForDeclaredFields(CompoundList.of(entries, new Entry(matcher, fieldVisitorWrappers)));
}
/**
* {@inheritDoc}
*/
public ClassVisitor wrap(TypeDescription instrumentedType,
ClassVisitor classVisitor,
Implementation.Context implementationContext,
TypePool typePool,
FieldList<FieldDescription.InDefinedShape> fields,
MethodList<?> methods,
int writerFlags,
int readerFlags) {
Map<String, FieldDescription.InDefinedShape> mapped = new HashMap<String, FieldDescription.InDefinedShape>();
for (FieldDescription.InDefinedShape fieldDescription : fields) {
mapped.put(fieldDescription.getInternalName() + fieldDescription.getDescriptor(), fieldDescription);
}
return new DispatchingVisitor(classVisitor, instrumentedType, mapped);
}
可以看到最终是attributeAppenderFactory.make
产生一个FieldAttributeAppender
来修改字节码。
public FieldVisitor wrap(TypeDescription instrumentedType, FieldDescription.InDefinedShape fieldDescription, FieldVisitor fieldVisitor) {
return new FieldAttributeVisitor(fieldVisitor,
fieldDescription,
attributeAppenderFactory.make(instrumentedType),
annotationValueFilterFactory.on(fieldDescription));
}
前面说过FieldAttributeAppender
的apply方法。就是负责应用实现,现在可以看一个具体的apply实现。这里就调到了具体的api逻辑
public static void apply(AnnotationVisitor annotationVisitor, TypeDescription valueType, String name, Object value) {
if (valueType.isArray()) { // The Android emulator reads annotation arrays as annotation types. Therefore, this check needs to come first.
AnnotationVisitor arrayVisitor = annotationVisitor.visitArray(name);
int length = Array.getLength(value);
TypeDescription componentType = valueType.getComponentType();
for (int index = 0; index < length; index++) {
apply(arrayVisitor, componentType, NO_NAME, Array.get(value, index));
}
arrayVisitor.visitEnd();
} else if (valueType.isAnnotation()) {
handle(annotationVisitor.visitAnnotation(name, valueType.getDescriptor()), (AnnotationDescription) value, AnnotationValueFilter.Default.APPEND_DEFAULTS);
} else if (valueType.isEnum()) {
annotationVisitor.visitEnum(name, valueType.getDescriptor(), ((EnumerationDescription) value).getValue());
} else if (valueType.represents(Class.class)) {
annotationVisitor.visit(name, Type.getType(((TypeDescription) value).getDescriptor()));
} else {
annotationVisitor.visit(name, value);
}
}
和ForDeclareFields
的接口类似,但是稍微复杂一点,注意除了method
方法以外还有构造器和invoke方法
复合内置一个AsmVisitorWrapper
数组。
class Compound implements AsmVisitorWrapper {
private final List asmVisitorWrappers;
...
...
}
MemberRemoval 移出方法成员
MemberSubstitution 替换方法成员
ModifierAdjustment 修改修饰符权限
TypeReferenceAdjustment 引用类型的修改
MemberAttributeExtension 虽然不是子类,但是也是类似使用来扩展属性的值。
这个类的有11000行。实现类使用注解api,可以用来做切面。
官方描述
advice 包装器 copy模板方法里代码到在一个被matched
方法的入口和结束。为了实现这一点,一个类被标注了@OnMethodEnter
或者@OnMethodExit
的静态方法,会被目标类调用。
被标注了@OnMethodEnter
的方法可以使用@Argument
(使对被标注的参数访问被替换为对被修改方法的访问,也就是可以把目标对象的变量反向传递给我们定义的切面逻辑)标注目标的参数。参数@This
,代表这个整个被修改对象的指针。也可以为目标对象提供一个新的值(@Argument
可以带参数)。 如果没有注解,那就意味者被标记为n
。只有所有的方法和被标注的类的类型匹配,那么才算命中。
被标注了OnMethodExit
的方法可以被标注@Argument
和@This
,额外的可以标注@Return
去接受原始方法的返回值。通过重新标记这个返回值,可以做到替代返回的值。如过一个方法返回了一个异常,那么参数会被设置为默认值,比如int =0
,Integer=null
。参数的类型必须和被修改方法的类型之一。@Thrown
注解可以传递目标方法抛出的异常,如果正常结束,那么异常会被赋值为null
。 这么做的化,我们有机会拦截异常。如果一个方法标注了@OnMethodEnter
注解,那么这个方法的返回值,可以被标注了@Enter
的参数访问到,当然类型必须一至。 如果这个参数被标注了read-only
,那么这个返回值的super
类型会被返回。如果方法的返回值是void
,那么任何返回的值都会被 OnMethodExit
丢弃掉。
如果被增强(advice)的方法逻辑抛出一个异常,那么目标方法就会被提前终止。如果方法被标注了@OnMethodEnter
,抛出了异常,那么@OnMethodEnter
注解就不会被调用。如果被增强的方法抛出了一个异常只有把OnMethodExit.onThrowable()
属性设置为true,@OnMethodEnter
才被调用。但是如果设置了false
,@Thrown
注解就不会被调用。
Byte buddy 不会建设在inlined advice
中的任何类型是可见的。需要用户来判断,是否能对访问到一个方法内的变量。如果不能会在运行时抛出IllegalAccessError
异常。
advice
可以被用来作为AsmVisitorWrapper
,用来增强现有的修改的修改类型。也就是说advice
可以作为一个Implementation
,来增强目标类原有方法的逻辑。如果某个类不需要或者比较特殊,可以在implementation
覆盖Advice#wrap(Implementation)
逻辑,来自定义。
当把 advice 类作为一个 visitor wrapper
时,本地和抽象方法会被跳过,不匹配。
注意
:生成inlined methods
是不可能触发断点的。但是原始的目标方法可以设置断点。这个特性允许在单元测试时无需改动源码就能调用增强逻辑。
未定义的组件(没有经过正式的声明与编译,直接修改字节码)可能会在运行时导致异常。
当advice
和AgentBuilder
一起使用时。经常要注解在agent .jar
文件中不要包含依赖具体库的代码(和目标应用发生冲突)。为了可以依赖库中找到 advice code
,Byte buddy 提供了一个net.bytebuddy.agent.builder.AgentBuilder.Transformer.ForAdvice
的implementation,可以注册agent的的文件寻找器(class file locators),这么做可以在运行时汇总所有的增强逻辑,和响应具体的用户使用。
负责寻找定位 参数在字节码中的本地变量
的偏移量。比如public String hello(int id)
,参数名"id"
就放在本地变量
中。
instrumented
表示被修改的目标方法,advice
表示增强。
方法名 | ||
---|---|---|
int argument(int offset) | 定位instrumented 方法的偏移 |
|
int returned() | 定位instrumented return value的offset |
|
int thrown() | 定位instrumented exception的offset |
|
int exit() | 定位exit advice 的 的偏移 | |
int enter() | 定位enter advice 方法的偏移 | |
int named(String name) | 根据name定位变量在本地变量 方法的偏移 |
ArgumentHandler 的实现针对于遍历增强advice的方法。
可以看到保留有 对被修改类型和增强的引用。以及退出类型,可获得的本地变量关系。
主要方法的实现
public int exit() {
return instrumentedMethod.getStackSize();
}
/**
* {@inheritDoc}
*/
public int named(String name) {
return instrumentedMethod.getStackSize()
+ exitType.getStackSize().getSize()
+ StackSize.of(namedTypes.headMap(name).values());
}
/**
* {@inheritDoc}
*/
public int enter() {
return instrumentedMethod.getStackSize()
+ exitType.getStackSize().getSize()
+ StackSize.of(namedTypes.values());
}
继承default的实现,专门针对进入方法的类型。可以看出return()和thrwown()方法不实现。
protected static class ForMethodEnter extends Default {
/**
* Creates a new argument handler for an enter advice method.
*
* @param instrumentedMethod The instrumented method.
* @param adviceMethod The advice method.
* @param exitType The exit type or {@code void} if no exit type is defined.
* @param namedTypes A mapping of all available local variables by their name to their type.
*/
protected ForMethodEnter(MethodDescription instrumentedMethod,
MethodDescription adviceMethod,
TypeDefinition exitType,
TreeMap<String, TypeDefinition> namedTypes) {
super(instrumentedMethod, adviceMethod, exitType, namedTypes);
}
/**
* {@inheritDoc}
*/
public int returned() {
throw new IllegalStateException("Cannot resolve the return value offset during enter advice");
}
/**
* {@inheritDoc}
*/
public int thrown() {
throw new IllegalStateException("Cannot resolve the thrown value offset during enter advice");
}
/**
* {@inheritDoc}
*/
public int mapped(int offset) {
return instrumentedMethod.getStackSize()
+ exitType.getStackSize().getSize()
+ StackSize.of(namedTypes.values())
- adviceMethod.getStackSize() + offset;
}
}
针对退出方法的处理
protected static class ForMethodExit extends Default {
/**
* The enter type or {@code void} if no enter type is defined.
*/
private final TypeDefinition enterType;
/**
* The stack size of a possibly stored throwable.
*/
private final StackSize throwableSize;
/**
* Creates a new argument handler for an exit advice method.
*
* @param instrumentedMethod The instrumented method.
* @param adviceMethod The advice method.
* @param exitType The exit type or {@code void} if no exit type is defined.
* @param namedTypes A mapping of all available local variables by their name to their type.
* @param enterType The enter type or {@code void} if no enter type is defined.
* @param throwableSize The stack size of a possibly stored throwable.
*/
protected ForMethodExit(MethodDescription instrumentedMethod,
MethodDescription adviceMethod,
TypeDefinition exitType,
TreeMap<String, TypeDefinition> namedTypes,
TypeDefinition enterType,
StackSize throwableSize) {
super(instrumentedMethod, adviceMethod, exitType, namedTypes);
this.enterType = enterType;
this.throwableSize = throwableSize;
}
/**
* {@inheritDoc}
*/
public int returned() {
return instrumentedMethod.getStackSize()
+ exitType.getStackSize().getSize()
+ StackSize.of(namedTypes.values())
+ enterType.getStackSize().getSize();
}
/**
* {@inheritDoc}
*/
public int thrown() {
return instrumentedMethod.getStackSize()
+ exitType.getStackSize().getSize()
+ StackSize.of(namedTypes.values())
+ enterType.getStackSize().getSize()
+ instrumentedMethod.getReturnType().getStackSize().getSize();
}
/**
* {@inheritDoc}
*/
public int mapped(int offset) {
return instrumentedMethod.getStackSize()
+ exitType.getStackSize().getSize()
+ StackSize.of(namedTypes.values())
+ enterType.getStackSize().getSize()
+ instrumentedMethod.getReturnType().getStackSize().getSize()
+ throwableSize.getSize()
- adviceMethod.getStackSize()
+ offset;
}
}
针对被修改方法参数的定位。
方法名 | ||
---|---|---|
int variable(int index) | 定位instrumented 方法的偏移 |
|
int prepare(MethodVisitor methodVisitor) | 处理参数 | |
ForAdvice bindEnter(MethodDescription adviceMethod) | 绑定一个advice方法作为方法开始的处理 | |
ForAdvice bindExit(MethodDescription adviceMethod, boolean skipThrowable) | 绑定一个advice方法作为方法结束的处理 | |
boolean isCopyingArguments() | 在执行目标类的方法前,是否copy原始的方法参数 | |
List |
按照声明顺序返回被命名类的类型 |
绑定了入口和退出的方法处理
public ForAdvice bindEnter(MethodDescription adviceMethod) {
return new ForAdvice.Default.ForMethodEnter(instrumentedMethod, adviceMethod, exitType, namedTypes);
}
/**
* {@inheritDoc}
*/
public ForAdvice bindExit(MethodDescription adviceMethod, boolean skipThrowable) {
return new ForAdvice.Default.ForMethodExit(instrumentedMethod,
adviceMethod,
exitType,
namedTypes,
enterType,
skipThrowable ? StackSize.ZERO : StackSize.SINGLE);
}
/**
* {@inheritDoc}
*/
public List<TypeDescription> getNamedTypes() {
List<TypeDescription> namedTypes = new ArrayList<TypeDescription>(this.namedTypes.size());
for (TypeDefinition typeDefinition : this.namedTypes.values()) {
namedTypes.add(typeDefinition.asErasure());
}
return namedTypes;
}
简单的hanlder实现
其中prepare方法的实现。修改了目标方法的每一个参数。
public int prepare(MethodVisitor methodVisitor) {
StackSize stackSize;
if (!instrumentedMethod.isStatic()) {
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitVarInsn(Opcodes.ASTORE, instrumentedMethod.getStackSize()
+ exitType.getStackSize().getSize()
+ StackSize.of(namedTypes.values())
+ enterType.getStackSize().getSize());
stackSize = StackSize.SINGLE;
} else {
stackSize = StackSize.ZERO;
}
for (ParameterDescription parameterDescription : instrumentedMethod.getParameters()) {
Type type = Type.getType(parameterDescription.getType().asErasure().getDescriptor());
methodVisitor.visitVarInsn(type.getOpcode(Opcodes.ILOAD), parameterDescription.getOffset());
methodVisitor.visitVarInsn(type.getOpcode(Opcodes.ISTORE), instrumentedMethod.getStackSize()
+ exitType.getStackSize().getSize()
+ StackSize.of(namedTypes.values())
+ enterType.getStackSize().getSize()
+ parameterDescription.getOffset());
stackSize = stackSize.maximum(parameterDescription.getType().getStackSize());
}
return stackSize.getSize();
}
定义了两个常量,
/**
* A factory for creating a simple argument handler.
*/
SIMPLE {
@Override
protected ForInstrumentedMethod resolve(MethodDescription instrumentedMethod,
TypeDefinition enterType,
TypeDefinition exitType,
Map<String, TypeDefinition> namedTypes) {
return new ForInstrumentedMethod.Default.Simple(instrumentedMethod,
exitType,
new TreeMap<String, TypeDefinition>(namedTypes),
enterType);
}
},
/**
* A factory for creating an argument handler that copies all arguments before executing the instrumented method.
*/
COPYING {
@Override
protected ForInstrumentedMethod resolve(MethodDescription instrumentedMethod,
TypeDefinition enterType,
TypeDefinition exitType,
Map<String, TypeDefinition> namedTypes) {
return new ForInstrumentedMethod.Default.Copying(instrumentedMethod,
exitType,
new TreeMap<String, TypeDefinition>(namedTypes),
enterType);
}
};
类似ArumentHander,这个是用来处理方法的。用来计算目标方法的字节码大小。
make
方法是负责创建一个PostProcessor的。public void apply(MethodVisitor methodVisitor,
MethodDescription.InDefinedShape adviceMethod,
TypeDescription instrumentedType,
MethodDescription instrumentedMethod,
boolean exit) {
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
adviceMethod.getDeclaringType().getInternalName(),
adviceMethod.getInternalName(),
adviceMethod.getDescriptor(),
false);
}
直接用定义在二进制的字节码文件的偏移量
来代替传统的遍历访问。
这个map保存这样的映射关系。
Target
resolve
(TypeDescription instrumentedType,Target
封装了 目标方法的offset
信息首先它的实现类中。都各自有ReadOnly
和ReadWrite
实现。
ReadOnly
实现了读取目标类的字节码,并被StackManipulation
包装。
比如ForDefaultValue
的实现read
: 直接返回类型对应的StackManipulation
public StackManipulation resolveRead() {
return new StackManipulation.Compound(DefaultValue.of(typeDefinition), readAssignment);
}
。。。
// 解析typeDefinition,返回不同类型对应的StackManipulation
public static StackManipulation of(TypeDefinition typeDefinition) {
if (typeDefinition.isPrimitive()) {
if (typeDefinition.represents(long.class)) {
return LONG;
} else if (typeDefinition.represents(double.class)) {
return DOUBLE;
} else if (typeDefinition.represents(float.class)) {
return FLOAT;
} else if (typeDefinition.represents(void.class)) {
return VOID;
} else {
return INTEGER;
}
} else {
return REFERENCE;
}
}
比如ForDefaultValue
的实现write
: Removal是StackManipulation的子类,代表着删除
public StackManipulation resolveWrite() {
return Removal.of(typeDefinition);
}
实现类
本地变量
的处理superess是抑制的意思,这个类的作用拦截Suppress exception
。在前后加入自定一的动做
封装了拦截的动作。上面的bind
函数就是返回这么一个对象,包含了对handler
的额外处理
suppression handler
是被调用Suppressing是 SuppressingHandler的实现。
要知道 字节码中,if,switch,或者 try catch等会经常使用标签label
,方便根据判断跳转(jump
)到不同的代码逻辑。
可以看到这里就是使用几个lable,完成这样的工作
protected static class Bound implements SuppressionHandler.Bound {
/**
* The suppressed throwable type.
*/
private final TypeDescription suppressedType;
/**
* The stack manipulation to apply within a suppression handler.
*/
private final StackManipulation exceptionHandler;
/**
* A label indicating the start of the method.
*/
private final Label startOfMethod;
/**
* A label indicating the end of the method.
*/
private final Label endOfMethod;
/**
* Creates a new active, bound suppression handler.
*
* @param suppressedType The suppressed throwable type.
* @param exceptionHandler The stack manipulation to apply within a suppression handler.
*/
protected Bound(TypeDescription suppressedType, StackManipulation exceptionHandler) {
this.suppressedType = suppressedType;
this.exceptionHandler = exceptionHandler;
startOfMethod = new Label();
endOfMethod = new Label();
}
/**
* {@inheritDoc}
*/
public void onPrepare(MethodVisitor methodVisitor) {
methodVisitor.visitTryCatchBlock(startOfMethod, endOfMethod, endOfMethod, suppressedType.getInternalName());
}
/**
* {@inheritDoc}
*/
public void onStart(MethodVisitor methodVisitor) {
methodVisitor.visitLabel(startOfMethod);
}
}
负责对instrumented method
的控制流,链式的串联起来。
重定位的操作是在字节码中jump
到不同代码逻辑,一般依赖字节码中存在的lable
。但是最终写到字节码前,可把很多条件保留在代码逻辑里,具体生成是按照代码逻辑生成不同lable
。这个RelocationHandler
就是组合逻辑,在不同位置生成label
是Dispatcher 的实现,表明没有解决的的dispacther
。仅仅内置了一个Map
保存所有被enter
声明
Resolved.ForMethodEnter asMethodEnter
(List extends OffsetMapping.Factory>> userFactories,
ClassReader classReader,
Unresolved methodExit,
PostProcessor.Factory postProcessorFactory);
转化为入口方法
Resolved.ForMethodExit asMethodExit
(List extends OffsetMapping.Factory>> userFactories,
ClassReader classReader,
Unresolved methodEnter,
PostProcessor.Factory postProcessorFactory);
转化为出口方法
interface Bound {
/** 准备advice的异常处理时
* Prepares the advice method's exception handlers.
*/
void prepare();
/** 初始化advice的的`本地变量`
* Initialized the advice's methods local variables.
*/
void initialize();
/** 应用变化时
* Applies this dispatcher.
*/
void apply();
}
入口方法的处理
Map
就是处理类型的兼容。
退出方法的处理
label
,所以控制跳转到不同label
能实现代码逻辑的替换。注解 | 值 | ||
---|---|---|---|
@OnMethodEnter |
表示这个方法会在,进入目标方法时调用,这个注解声明的方法必须是static 。当目标的方法是constructor 构造器时,@This 只能写field ,不能读field,或者调用方法 |
skipOn() | 跳过一些方法 |
prependLineNumber() | 如果为true,会改目标方法的行号 | ||
inline() | 标识方法应该被内联到目标的方法中去 | ||
suppress() | 忽略某些异常 | ||
@OnMethodExit |
表示这个方法会在,目标方法结束时调用,这个注解声明的方法必须是static 。如果方法提前终止了,那么这个就不i呗调用 |
repeatOn() | 标识目标方法是否被重复执行 |
onThrowable() | 一般被织入的方法抛出了某些异常,可以有响应的handler 处理 |
||
backupArguments() | 备份所有被执行方法的类型,开始会影响效率 | ||
inline() | 标识方法应该被内联到目标的方法中去 | ||
suppress() | 忽略某些异常 | ||
@This |
表示被注解的参数,应该是被修改对象的引用 | optional() | 决定被修改的类型是否需要被设置为null,如果目标方法类型是static 或者在一个constructor |
readOnly() | 只读不能修改 | ||
typing() | 类型转化,默认要求静态转化,就是转化不能改变类型。动态是可以改变 | ||
@Argument |
被标注到目标类型的参数上,表示被标注的参数会被value()代表的索引拿到 | ||
readOnly() | 只读 | ||
typing() | 转换这个类型使用的方式,默认是静态转换(类型不会被改动),动态转换是void.class可以被改成其他类 | ||
optional() | 备选值,如果索引不存在,会使用这个提供的值。默认是关闭的 | ||
@AllArguments |
使用一个数组来包含目标方法的参数,目标的参数必须是一个数组。 | ||
readOnly() | 只读 | ||
typing() | 类型开关 | ||
@Return |
标注在参数上,来引用目标方法的返回值 | readOnly() | 只读 |
typing() |
类型转化,默认是静态转换 | ||
@Thrown |
获取抛出的异常 | ||
readOnly() | 只读 | ||
typing() | 默认是动态转化,可以改变类型 | ||
@FieldValue |
被注解的参数,可以引用,目标method内的定义的局部变量 | String value() | 局部变量的名称 |
declaringType() | 被声明的类型 | ||
readOnly() | 只读的 | ||
typing() | 默认是静态转化 | ||
@Origin |
使用一个String代表目标方法的 | String value() default DEFAULT | 默认值是"" |
@Enter |
标注在参数上,指向被标注@OnMethodEnter 的advice方法的返回值, |
||
readOnly() | 只读标记 | ||
typing() | 转换 | ||
@Exit |
标注在参数上,指向被标注@OnMethodExit 的advice方法的返回值, |
||
readOnly() | 只读标记 | ||
typing() | 转换 | ||
@Local |
声明被注解的参数当做一个本地变量,被Byte Buddy ,织入目标方法中。本地变量可以被@OnMethodEnter 和 @link OnMethodExit 读写。然而如果本地变量被exit advice 引用了,它必须也在enter 的advice所声明。就是用来交换变量的。 |
String value() | name |
@StubValue |
mock值,总是返回一个设定的值 | ||
@Unused |
让被标记的参数,总是返回默认值,比如int 返回0, 其他类型返回null |