bytebuddy之advice详解 注解详解
注解列表
- 一、 注解列表
- 二、 demo 解析
- 2.1 测试工具介绍
- 2.1.1 目标的类得classloader
- 2.1.2 获取当前jvm得 Instrumentation
- 2.2 @OnMethodEnter 和 @OnMethodExit的简单应用
- 2.2.1 @OnMethodEnter 进入方法时
- 2.2.1.1 skipOn() 跳过
- 1. void 默认不跳过的
- 2 OnDefaultValue 默认值
- 3 OnNonDefaultValue 非默认值
- 4 自定义类型
- 2.2.1.2 inline() 默认true ,内联编译
- 2.2.1.3 suppress 处理告警,默认是抑制告警
- 2.2.2 @OnMethodExit
- 2.2.2.1 repeatOn() 重复
- 2.2.2.2 onThrowable() 异常的捕获和 @Thrown搭配使用
- 2.3 @This注解
- 2.3.1 default : optional() = false,typing()=Assigner.Typing.STATIC;
- 2.3.2 default : optional() = true,typing()=Assigner.Typing.STATIC;
- 2.3.3 default : optional() = true,typing()=Assigner.Typing.DYNAMIC;
- 2.4 @Argument 和 @AllArguments
- 2.4.1 @AllArguments
- 2.4.2 @Argument
- 2.5 @Return 获取返回值
- 2.6 @Thrown 获取异常
- 2.7 @FieldValue 获取目标类中的变量
- 2.8 @Origin
- 2.9 @Enter
- 2.10 @Exit
- 2.11 @Local
- 1 获取
- 2.12 @StubValue
- 2.13 @Unused
bytebuddy实现原理分析 &源码分析(一)
bytebuddy实现原理分析 &源码分析 (二)
bytebuddy实现原理分析 &源码分析 (三)
bytebuddy实现原理分析 &源码分析 (四)
一、 注解列表
注解 | 值 | ||
---|---|---|---|
@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 |
二、 demo 解析
byte budddy 单测
测试修改的类。
byte buddy 实用手册
2.1 测试工具介绍
2.1.1 目标的类得classloader
ChildFirst
打破双亲委派,当前classLoader直接加载。
readToNames
返回Map<String , byte[]>
返回类和类得字节码。自己读取也可以。
MANIFEST
持久化策略,代表加载得会被保存下来
ClassLoader classLoader = new ByteArrayClassLoader.ChildFirst(getClass().getClassLoader(),ClassFileLocator.ForClassLoader.readToNames(Cooker.class),ByteArrayClassLoader.PersistenceHandler.MANIFEST);
2.1.2 获取当前jvm得 Instrumentation
ByteBuddyAgent.install();
一旦调用就会在内存中维护一个实例,实例中得Instrumentation
是当前jvm得应用。
2.2 @OnMethodEnter 和 @OnMethodExit的简单应用
代码得位置
定义了两个一模一样得目标类Cooker
和Cooker2
package bytebuddys.advice.target;public class Cooker {String name = "foo";public Cooker() {System.out.println("");System.out.println("Im Cooker");System.out.println("Constructor();");}public void hello() {System.out.println("public void hello();");}public String makeFood(String foodName, int deskId, Double[] materialsPrice) {System.out.println(materialsPrice.getClass().getName());System.out.println("public String makeFood(String foodName, int deskId, Double[] materialsPrice)! ");return foodName + ":" + deskId + ":" + materialsPrice.length;}public static void taste(String foodName) {System.out.println("public static void taste(String foodName)! ");}}
我们给Cooker
(除了构造器)所有方法的开始和结束织入额外的逻辑
package bytebuddys.advice;import net.bytebuddy.asm.Advice;public class AdviceLogic {@Advice.OnMethodEnterpublic static void onMethodEnter(){System.out.println("- - - - - - - - - - -");System.out.println("enter!");}@Advice.OnMethodExitpublic static void onMethodExit(){System.out.println("exit!");}}
测试一下
我们对Cooker
改造,不动 Cooker2
package bytebuddys.advice;import bytebuddys.advice.target.Cooker;
import bytebuddys.advice.target.Cooker2;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ByteArrayClassLoader;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;import java.lang.instrument.ClassFileTransformer;public class TestAdvice {public static TestAdvice INSTANCE = new TestAdvice();ClassLoader classLoader;/*** 加载未被修改的类*/public void initClassLoader() throws Exception {classLoader = new ByteArrayClassLoader.ChildFirst(getClass().getClassLoader(),ClassFileLocator.ForClassLoader.readToNames(Cooker.class),ByteArrayClassLoader.PersistenceHandler.MANIFEST);}/*** modify*/public void modifyTarget() throws Exception {ByteBuddyAgent.install();ClassFileTransformer classFileTransformer = new AgentBuilder.Default().with(AgentBuilder.PoolStrategy.Default.EXTENDED).with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)//.ignore()// 设定匹配范围.type(ElementMatchers.is(Cooker.class), ElementMatchers.is(classLoader)).transform(new AgentBuilder.Transformer() {@Overridepublic DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {// 对任何类都剩下return builder.visit(Advice.to(AdviceLogic.class).on(ElementMatchers.not(ElementMatchers.isConstructor()).and(ElementMatchers.any())));}}).installOnByteBuddyAgent();}public void print() throws Exception {Class<Cooker> cookerType = (Class<Cooker>) classLoader.loadClass(Cooker.class.getName());Object Cooker = cookerType.getDeclaredConstructor().newInstance();cookerType.getDeclaredMethod("hello").invoke(Cooker);cookerType.getDeclaredMethod("taste", String.class).invoke(null, "pototo");cookerType.getMethod("makeFood", String.class, int.class, Double[].class).invoke(Cooker, "pototo", 1, new Double[]{1.0, 2.0});Class<Cooker2> cooker2Type = (Class<Cooker2>) classLoader.loadClass(Cooker2.class.getName());Object Cooker2 = cooker2Type.getDeclaredConstructor().newInstance();cooker2Type.getDeclaredMethod("hello").invoke(Cooker2);cooker2Type.getDeclaredMethod("taste", String.class).invoke(Cooker2, "pototo");cooker2Type.getDeclaredMethod("makeFood", String.class, int.class, Double[].class).invoke(Cooker2, "pototo", 1, new Double[]{1.0, 2.0});}public static void main(String[] args) throws Exception {INSTANCE.initClassLoader();INSTANCE.modifyTarget();INSTANCE.print();}
}
Im Cooker
Constructor();
- - - - - - - - - - -
enter!
public void hello();
exit!
- - - - - - - - - - -
enter!
public static void taste(String foodName)!
exit!
- - - - - - - - - - -
enter!
[Ljava.lang.Double;
public String makeFood(String foodName, int deskId, Double[] materialsPrice)!
exit!Im Cooker2
Constructor();
public void hello();
public static void taste(String foodName)!
[Ljava.lang.Double;
public String makeFood(String foodName, int deskId, Double[] materialsPrice)!
2.2.1 @OnMethodEnter 进入方法时
skipOn()
跳过某个方法inline()
, 内联(默认)还是调用,区别是内联相当在目标类加入代码逻辑,会更快。调用就是调用第三方的方法。suppress()
将方法抛出的异常交给一个异常handler
处理,默认是NoExceptionHandler.class
相当于就是,抑制异常,不处理。
2.2.1.1 skipOn() 跳过
这个跳过方法的判定比较奇葩,他是根据advice
中返回值,来确定是否跳过。注意不能跳过构造器方案。
OnDefaultValue
当advice方法的返回值是{false for boolean , 0 for byte、short、char、int、long、float 、double , 或者null for 引用类型 }
那么就跳过目标方法。OnNonDefaultValue
就是刚好相反void
默认 代表不跳过任何方法自定义类型
,基本类型的坏处是,无法保留额外的信息,仅仅靠判断0或者非零。
自定义类型,可以携带额外的信息。
目标类
我们要跳过除了构造器以外的所有方法。
public class Cooker {String name = "foo";public Cooker() {System.out.println("");System.out.println("Im Cooker`s Constructor()");}public void hello() {System.out.println("+ method name : public void hello();");}public String makeFood(String foodName, int deskId, Double[] materialsPrice) {System.out.println("+ method name : public String makeFood(String foodName, int deskId, Double[] materialsPrice)! ");return foodName + ":" + deskId + ":" + materialsPrice.length;}public static void taste(String foodName) {System.out.println("+ method name : public static void taste(String foodName)! ");}public Cooker getInstance() {System.out.println("+ method name : public Cooker getInstance()");return this;}
}
测试
public class TestAdvice {public void defineNewClass() throws Exception {Class<?> cookerType = new ByteBuddy().redefine(Cooker.class).visit(Advice.to(AdviceLogic.class).on(ElementMatchers.not(ElementMatchers.isConstructor()))).make().load(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassLoadingStrategy.Default.WRAPPER).getLoaded();Object Cooker = cookerType.getDeclaredConstructor().newInstance();cookerType.getDeclaredMethod("hello").invoke(Cooker);cookerType.getDeclaredMethod("taste", String.class).invoke(null, "pototo");cookerType.getMethod("makeFood", String.class, int.class, Double[].class).invoke(Cooker, "pototo", 1, new Double[]{1.0, 2.0});cookerType.getDeclaredMethod("getInstance").invoke(Cooker);}public static void main(String[] args) throws Exception {INSTANCE.defineNewClass();}
}
1. void 默认不跳过的
public class AdviceLogic {@Advice.OnMethodEnterpublic static void enter() {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("enter!");}}
可以看到,enter
方法返回了一个 void
的方法。
打印
没有跳过,因此他执行了所有的方法
Im Cooker`s Constructor()- - - - - - - - - - -
enter!
+ method name : public void hello();- - - - - - - - - - -
enter!
+ method name : public static void taste(String foodName)! - - - - - - - - - - -
enter!
+ method name : public String makeFood(String foodName, int deskId, Double[] materialsPrice)! - - - - - - - - - - -
enter!
+ method name : public Cooker getInstance()
2 OnDefaultValue 默认值
后面我们可以知道,enter
是可以拿到被修改方式实例的引用和参数的。所有具体可以生成不同的值。如果 返回值是之前描述的各种基本类型的默认值
,或者null
。那么就跳过
目标的方法。
如下示例中,我们返回了0 。结果就是目标所有的方法都不执行。
public class AdviceLogic {@Advice.OnMethodEnter( skipOn = Advice.OnDefaultValue.class)public static int enter() {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("enter!");return 0;}}
打印
什么也没有执行
Im Cooker`s Constructor()- - - - - - - - - - -
enter!- - - - - - - - - - -
enter!- - - - - - - - - - -
enter!- - - - - - - - - - -
enter!
3 OnNonDefaultValue 非默认值
刚好相反,如果上面的例子中,return
不为0,那么就跳过
4 自定义类型
除了用基本类型标识外还可以,使用自定义的类型。如果自定义类型返回会null
,那么就不跳过。
advice-不跳过
public class AdviceLogic {@Advice.OnMethodEnter( skipOn = Cooker.class)public static Cooker enter() {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("enter!");return null;}}
打印
输出了所有
Im Cooker`s Constructor()- - - - - - - - - - -
enter!
+ method name : public void hello();- - - - - - - - - - -
enter!
+ method name : public static void taste(String foodName)! - - - - - - - - - - -
enter!
+ method name : public String makeFood(String foodName, int deskId, Double[] materialsPrice)! - - - - - - - - - - -
enter!
+ method name : public Cooker getInstance()
advice-跳过
public class AdviceLogic {@Advice.OnMethodEnter( skipOn = Cooker.class)public static Cooker enter() {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("enter!");return new Cooker();}}
打印
仅仅打印了 new Cooker的构造器,跳过了所有。
Im Cooker`s Constructor()- - - - - - - - - - -
enter!Im Cooker`s Constructor()- - - - - - - - - - -
enter!Im Cooker`s Constructor()- - - - - - - - - - -
enter!Im Cooker`s Constructor()- - - - - - - - - - -
enter!Im Cooker`s Constructor()
2.2.1.2 inline() 默认true ,内联编译
2.2.1.3 suppress 处理告警,默认是抑制告警
Class<? extends Throwable> suppress() default NoExceptionHandler.class;
把告警默认委托给一个handler,一个什么也不处理的handler。
比如抑制advice逻辑 抛出的IO异常
@Advice.OnMethodEnter(skipOn = Cooker.class, suppress = IOException.class)public static Cooker enter() throws IOException{throw new IOException();System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("enter!");return null;}
2.2.2 @OnMethodExit
repeatOn()
和skipon()类似的判断方式也有OnDefaultValue
和OnNonDefaultValue
。但是标注了方法意味只要再重复一次onThrowable()
捕获抛出的异常,和@Thrown
搭配使用backupArguments()
备份参数,默认为true,会专门的copy原先的参数inline()
和suppress()
都是和@OnMethodExit
都是类似的。
2.2.2.1 repeatOn() 重复
@Advice.OnMethodExit(repeatOn = Advice.OnDefaultValue.class)public static int exit() {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("exit!");return 0;}
打印
可以看到不断的重复hello()
;只重复一次,但是hello中不断命中advice。就是套娃,可以通过,在方法中,设置一个常量,来动态的改变返回值,来控制返回次数。
exit!
+ method name : public void hello();- - - - - - - - - - -
exit!
+ method name : public void hello();- - - - - - - - - - -
exit!
+ method name : public void hello();- - - - - - - - - - -
exit!
+ method name : public void hello();
....
2.2.2.2 onThrowable() 异常的捕获和 @Thrown搭配使用
抛出异常
public void hello() throws Exception {System.out.println("+ method name : public void hello();");throw new IOException();}
advice
public class AdviceLogic {@Advice.OnMethodExit(onThrowable = Throwable.class)public static int exit(@Advice.Thrown Throwable thrown) {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("exit!"+ thrown.getClass().getName());return 0;}}
打印
可以看到拿到了目标方法抛出的异常。
Im Cooker`s Constructor()
+ method name : public void hello();- - - - - - - - - - -
exit!java.io.IOException
Exception in thread "main" java.lang.reflect.InvocationTargetExceptionat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at bytebuddys.advice.TestAdvice.defineNewClass(TestAdvice.java:76)at bytebuddys.advice.TestAdvice.main(TestAdvice.java:108)
Caused by: java.io.IOExceptionat bytebuddys.advice.target.Cooker.hello(Cooker.java:19)... 6 more
2.3 @This注解
用来获取被修改的对象,
optional() = false
默认值。不能用在构造器
和静态方法
,否则会报错Exception in thread "main" java.lang.IllegalStateException: Cannot map this reference for static method or constructor start
。如果optional() = true
是,遇到构造器
和静态方法
这种没有实例的对象是,This
获取可以为null
。readOnly()
。 效果相当于final
,能不能修改传入的对象。typing()
。 类型转化,Assigner.Typing.STATIC;
不会进行强制转换,如果类型不符合直接报错,Assigner.Typing.DYNAMIC;
会进行强制转换。
Cooker目标类
public class Cooker {String name = "foo";public Cooker() {System.out.println("");System.out.println("Im Cooker`s Constructor()");}public void hello() {System.out.println("+ method name : public void hello();");}public String makeFood(String foodName, int deskId, Double[] materialsPrice) {System.out.println("+ method name : public String makeFood(String foodName, int deskId, Double[] materialsPrice)! ");return foodName + ":" + deskId + ":" + materialsPrice.length;}public static void taste(String foodName) {System.out.println("+ method name : public static void taste(String foodName)! ");}}
2.3.1 default : optional() = false,typing()=Assigner.Typing.STATIC;
不能被用于 静态方法,如果被用于静态方法,bytebuddy
里会报错,agentbuilder
会会忽略掉,就是什么也不发生
ADVICE
public class AdviceLogic {@Advice.OnMethodEnterpublic static void enter(@Advice.This Cooker thiz) {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("enter!");if (thiz == null) {System.out.println("- @This is null ! maybe used for static or contructors! ");} else {System.out.println("- @This!" + thiz.getClass().getName());}}
}
测试1
ElementMatchers.isMethod().and(ElementMatchers.not(ElementMatchers.isStatic()))
意味着只是匹配目标类的普通方法
public class TestAdvice {public static TestAdvice INSTANCE = new TestAdvice();ClassLoader classLoader;/*** 加载未被修改的类*/public void initClassLoader() throws Exception {classLoader = new ByteArrayClassLoader.ChildFirst(getClass().getClassLoader(),ClassFileLocator.ForClassLoader.readToNames(Cooker.class),ByteArrayClassLoader.PersistenceHandler.MANIFEST);}/*** modify*/public void modifyTarget() throws Exception {ByteBuddyAgent.install();ClassFileTransformer classFileTransformer = new AgentBuilder.Default().with(AgentBuilder.PoolStrategy.Default.EXTENDED).with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)//.ignore()// 设定匹配范围.type(ElementMatchers.is(Cooker.class), ElementMatchers.is(classLoader)).transform(new AgentBuilder.Transformer() {@Overridepublic DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {// 对比 enter 设置为 @Advice.This(optional = false)// exit 设置为 @Advice.This(optional = true )//return builder.visit(Advice.to(AdviceLogic.class).on(ElementMatchers.any()));return builder.visit(Advice.to(AdviceLogic.class).on(ElementMatchers.isMethod().and(ElementMatchers.not(ElementMatchers.isStatic()))));}}).installOnByteBuddyAgent();}public void print() throws Exception {Class<Cooker> cookerType = (Class<Cooker>) classLoader.loadClass(Cooker.class.getName());Object Cooker = cookerType.getDeclaredConstructor().newInstance();cookerType.getDeclaredMethod("hello").invoke(Cooker);cookerType.getDeclaredMethod("taste", String.class).invoke(null, "pototo");cookerType.getMethod("makeFood", String.class, int.class, Double[].class).invoke(Cooker, "pototo", 1, new Double[]{1.0, 2.0});}public static void main(String[] args) throws Exception {INSTANCE.initClassLoader();INSTANCE.modifyTarget();INSTANCE.print();}
}
打印
Im Cooker`s Constructor()- - - - - - - - - - -
enter!
- @This!bytebuddys.advice.target.Cooker
+ method name : public void hello();
+ method name : public static void taste(String foodName)! - - - - - - - - - - -
enter!
- @This!bytebuddys.advice.target.Cooker
+ method name : public String makeFood(String foodName, int deskId, Double[] materialsPrice)!
测试2
ElementMatchers.any()
表示任何方法,这意味着会出错,出错意味着原样输出
public void modifyTarget() throws Exception {ByteBuddyAgent.install();ClassFileTransformer classFileTransformer = new AgentBuilder.Default().with(AgentBuilder.PoolStrategy.Default.EXTENDED).with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)//.ignore()// 设定匹配范围.type(ElementMatchers.is(Cooker.class), ElementMatchers.is(classLoader)).transform(new AgentBuilder.Transformer() {@Overridepublic DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {// 对比 enter 设置为 @Advice.This(optional = false)// exit 设置为 @Advice.This(optional = true )return builder.visit(Advice.to(AdviceLogic.class).on(ElementMatchers.any()));
// return builder.visit(Advice.to(AdviceLogic.class).on(ElementMatchers.isMethod().and(ElementMatchers.not(ElementMatchers.isStatic()))));}}).installOnByteBuddyAgent();}
打印
Im Cooker`s Constructor()
+ method name : public void hello();
+ method name : public static void taste(String foodName)!
+ method name : public String makeFood(String foodName, int deskId, Double[] materialsPrice)!
2.3.2 default : optional() = true,typing()=Assigner.Typing.STATIC;
即使ElementMatchers.any()
,也会正常输出,但是This
打印为null
的输出
ADVICE
public class AdviceLogic {@Advice.OnMethodEnterpublic static void enter(@Advice.This(optional = true) Object thiz) {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("enter!");if (thiz == null) {System.out.println("- @This is null ! maybe used for static or contructors! ");} else {System.out.println("- @This!" + thiz.getClass().getName());}}
}
public void modifyTarget() throws Exception {ByteBuddyAgent.install();ClassFileTransformer classFileTransformer = new AgentBuilder.Default().with(AgentBuilder.PoolStrategy.Default.EXTENDED).with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)//.ignore()// 设定匹配范围.type(ElementMatchers.is(Cooker.class), ElementMatchers.is(classLoader)).transform(new AgentBuilder.Transformer() {@Overridepublic DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {// 对比 enter 设置为 @Advice.This(optional = false)// exit 设置为 @Advice.This(optional = true )return builder.visit(Advice.to(AdviceLogic.class).on(ElementMatchers.any()));
// return builder.visit(Advice.to(AdviceLogic.class).on(ElementMatchers.isMethod().and(ElementMatchers.not(ElementMatchers.isStatic()))));}}).installOnByteBuddyAgent();}
打印
- - - - - - - - - - -
enter!
- @This is null ! maybe used for static or contructors! Im Cooker`s Constructor()- - - - - - - - - - -
enter!
- @This!bytebuddys.advice.target.Cooker
+ method name : public void hello();- - - - - - - - - - -
enter!
- @This is null ! maybe used for static or contructors!
+ method name : public static void taste(String foodName)! - - - - - - - - - - -
enter!
- @This!bytebuddys.advice.target.Cooker
+ method name : public String makeFood(String foodName, int deskId, Double[] materialsPrice)!
2.3.3 default : optional() = true,typing()=Assigner.Typing.DYNAMIC;
advice
这里将一个Cooker
对象转化为一个Cooker2
。
如果是Assigner.Typing.STATIC
默认不会转换,直接原样输出。
Assigner.Typing.DYNAMIC
,会尝试装换,发现是Cooker2
,抛出异常。
public class AdviceLogic {@Advice.OnMethodEnterpublic static void enter(@Advice.This(optional = true , typing = Assigner.Typing.DYNAMIC) Cooker2 thiz) {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("enter!");if (thiz == null) {System.out.println("- @This is null ! maybe used for static or contructors! ");} else {System.out.println("- @This!" + thiz.getClass().getName());}}
}
打印
- - - - - - - - - - -
enter!
- @This is null ! maybe used for static or contructors! Im Cooker`s Constructor()- - - - - - - - - - -
enter!
Exception in thread "main" java.lang.reflect.InvocationTargetExceptionat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at bytebuddys.advice.TestAdvice.print(TestAdvice.java:67)at bytebuddys.advice.TestAdvice.main(TestAdvice.java:79)
Caused by: java.lang.ClassCastException: bytebuddys.advice.target.Cooker cannot be cast to bytebuddys.advice.target.Cooker2at bytebuddys.advice.target.Cooker.hello(Cooker.java:16)... 6 more
2.4 @Argument 和 @AllArguments
2.4.1 @AllArguments
advice
打印所有的入参
public class AdviceLogic {@Advice.OnMethodEnterpublic static void enter(@Advice.AllArguments Object[] args) {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("enter!");System.out.println("- @AllArguments !!");for (Object o : args) {System.out.println("- - - type : " + o.getClass().getName() + ", value : " + o.toString());}}
}
测试
builder.visit(Advice.to(AdviceLogic.class).on(ElementMatchers.named("makeFood")));
匹配cooker的makeFood
方法
public void modifyTarget() throws Exception {ByteBuddyAgent.install();ClassFileTransformer classFileTransformer = new AgentBuilder.Default().with(AgentBuilder.PoolStrategy.Default.EXTENDED).with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)//.ignore()// 设定匹配范围.type(ElementMatchers.is(Cooker.class), ElementMatchers.is(classLoader)).transform(new AgentBuilder.Transformer() {@Overridepublic DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {// 对比 enter 设置为 @Advice.This(optional = false)// exit 设置为 @Advice.This(optional = true )return builder.visit(Advice.to(AdviceLogic.class).on(ElementMatchers.named("makeFood")));
// return builder.visit(Advice.to(AdviceLogic.class).on(ElementMatchers.isMethod().and(ElementMatchers.not(ElementMatchers.isStatic()))));}}).installOnByteBuddyAgent();}
打印
- - - - - - - - - - -
enter!
- @AllArguments !!
- - - type : java.lang.String, value : pototo
- - - type : java.lang.Integer, value : 1
- - - type : [Ljava.lang.Double;, value : [Ljava.lang.Double;@48fa0f47
+ method name : public String makeFood(String foodName, int deskId, Double[] materialsPrice)!
2.4.2 @Argument
类似上一个注解,这个注解也是用来获取传入的参数。
但是需要指名参数的序号
,optional = true
意味着如果目标方法没有该参数,依旧会返回,不过值是null
。
advice
这就是一个打乱了次序,和填了一个目标方法不存的参数。却是依旧能正确返回的例子。
public class AdviceLogic {@Advice.OnMethodEnterpublic static void enter(@Advice.Argument(value = 1) int arg2,@Advice.Argument(value = 2) Double[] arg3,@Advice.Argument(value = 0) String arg1,@Advice.Argument(value = 3, optional = true) String arg4) {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("enter!");Object[] args = new Object[4];args[0] = arg1;args[1] = arg2;args[2] = arg3;args[3] = arg4;for (Object o : args) {if (o != null) {System.out.println("- - - type : " + o.getClass().getName() + ", value : " + o.toString());} else {System.out.println("- - - type : null , value : null ");}}}
}
打印
enter!
- - - type : java.lang.String, value : pototo
- - - type : java.lang.Integer, value : 1
- - - type : [Ljava.lang.Double;, value : [Ljava.lang.Double;@5e316c74
- - - type : null , value : null
+ method name : public String makeFood(String foodName, int deskId, Double[] materialsPrice)!
2.5 @Return 获取返回值
advice
只能标注在@Advice.OnMethodExit
上,用来承接返回值
public class AdviceLogic {@Advice.OnMethodExitpublic static void exit(@Advice.Return String result) {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("exit!");System.out.println("- - - type : " + result.getClass().getName() + ", value : " + result.toString());}}
打印
- - - - - - - - - - -
exit!
- - - type : java.lang.String, value : pototo:1:2
2.6 @Thrown 获取异常
捕获异常 2.2.2.2有讲
2.7 @FieldValue 获取目标类中的变量
value()
filedValue
@Advice.OnMethodEnter
和@Advice.OnMethodExit
,都可以用declaringType
filedValue 所在的类
Cooker
public class Cooker {String name = "foo";
}
advice
public class AdviceLogic {@Advice.OnMethodExitpublic static int exit(@Advice.FieldValue(value = "name",declaringType = Cooker.class) String name) {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("exit! name : "+ name);return 0;}}
打印
通通拿到了
Im Cooker`s Constructor()- - - - - - - - - - -
exit! name : foo
+ method name : public void hello();- - - - - - - - - - -
exit! name : foo
+ method name : public static void taste(String foodName)!
+ method name : public String makeFood(String foodName, int deskId, Double[] materialsPrice)! - - - - - - - - - - -
exit! name : foo
2.8 @Origin
利用反射,将目标字符串的签名,转化为方法和类格式,然后去调用。
advice
public class AdviceLogic {@Advice.OnMethodExitpublic static int exit(@AllArguments Object[] allArguments,@Origin Method method@Origin Class<?> method) {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("exit! name : "+ origin);return 0;}}
打印
- - - - - - - - - - -
exit! name : public bytebuddys.advice.target.Cooker()
+ method name : public void hello();- - - - - - - - - - -
exit! name : public void bytebuddys.advice.target.Cooker.hello()
+ method name : public static void taste(String foodName)!
+ method name : public String makeFood(String foodName, int deskId, Double[] materialsPrice)! - - - - - - - - - - -
exit! name : public java.lang.String bytebuddys.advice.target.Cooker.makeFood(java.lang.String,int,java.lang.Double[])
2.9 @Enter
标注在参数上,指向被标注@OnMethodEnter的advice方法的返回值,
readOnly() 只读标记
typing() 转换
advice
public class AdviceLogic {@Advice.OnMethodEnterpublic static int enter() {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("enter! name ");return 0;}@Advice.OnMethodExitpublic static int exit(@Advice.Enter int a) {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("exit! the enter return : "+ a);return 0;}}
打印
- - - - - - - - - - -
enter! name Im Cooker`s Constructor()- - - - - - - - - - -
exit! the enter return : 0- - - - - - - - - - -
enter! name
+ method name : public void hello();- - - - - - - - - - -
exit! the enter return : 0
+ method name : public static void taste(String foodName)! - - - - - - - - - - -
enter! name
+ method name : public String makeFood(String foodName, int deskId, Double[] materialsPrice)! - - - - - - - - - - -
exit! the enter return : 0
2.10 @Exit
标注在参数上,指向被标注@OnMethodExit的advice方法的返回值,
和@Enter
类似。
2.11 @Local
创建本地变量
中值,为方法创建一个局部变量。
常见的用途是
- 在方法内创建一个局部变量,然后可以被
@Advice.OnMethodEnter
和@Advice.OnMethodExit
同时获取到。
1 获取
比如一个本地方法foo
,就会被存储在本地变量
public static class Sample {public String foo() {return "foo";}}
可以通过如下获取
public static class LocalValueAdvice {@Advice.OnMethodEnterprivate static void enter(@Advice.Local("foo") Object foo) {if (foo != null) {throw new AssertionError();}else{//如果类没有的这个局部变量的变化,还可以创建一个foo ="new foo"}// 更改foo = "easy";if (!foo.equals(FOO)) {throw new AssertionError();}Sample.enter++;}
}// exit中就可以拿到@Advice.OnMethodExitprivate static void exit(@Advice.Local("foo") Object foo) {if (foo != null) {throw new AssertionError();}else{//如果类没有的这个局部变量的变化,还可以创建一个foo ="new foo"}// 更改foo = "easy";if (!foo.equals(FOO)) {throw new AssertionError();}Sample.enter++;}
}
2.12 @StubValue
mock值,总是返回一个设定的值.
- 必须使用
Object
迎接, - 返回默认值比如
null
,0
@Advice.OnMethodEnterpublic static int enter(@Advice.StubValue Object a) {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("enter! StubValue "+ a);return 0;}
2.13 @Unused
Unused ,被标注了这个注解的参数总是返回默认值
@Advice.OnMethodEnterpublic static int enter(@Advice.Unused Object arg) {System.out.println("");System.out.println("- - - - - - - - - - -");System.out.println("enter! StubValue ");if(arg!=null){System.out.println("arg0 -"+arg.getClass().getName() +" - value : "+arg.toString());}return 0;}
bytebuddy之advice详解 注解详解相关推荐
- Spring data JPA 之 Jackson 在实体里面的注解详解
8 Spring data JPA 之 Jackson 在实体里面的注解详解 经过前⾯课时的讲解,相信你已经对实体⾥⾯的 JPA 注解有了⼀定的了解,但是实际⼯作中你会发现实体⾥⾯不仅有 JPA 的注 ...
- 26.SpringBoot事务注解详解
转自:https://www.cnblogs.com/kesimin/p/9546225.html @Transactional spring 事务注解 1.简单开启事务管理 @EnableTrans ...
- mybatis注解详解
mybatis注解详解 首 先当然得下载mybatis-3.0.5.jar和mybatis-spring-1.0.1.jar两个JAR包,并放在WEB-INF的lib目录下 (如果你使用maven,则 ...
- 开启注解缓存_Spring Boot 2.x基础教程:进程内缓存的使用与Cache注解详解
随着时间的积累,应用的使用用户不断增加,数据规模也越来越大,往往数据库查询操作会成为影响用户使用体验的瓶颈,此时使用缓存往往是解决这一问题非常好的手段之一.Spring 3开始提供了强大的基于注解的缓 ...
- spring-boot注解详解(一)
spring-boot注解详解(一) @SpringBootApplication @SpringBootApplication = (默认属性)@Configuration + @EnableAut ...
- Spring零配置之@Configuration注解详解
转载自 Spring零配置之@Configuration注解详解 @Configuration介绍 Spring3.0之前要使用Spring必须要有一个xml配置文件,这也是Spring的核心文件,而 ...
- Spring Boot注解详解
文章目录 使用注解的优势 注解详解(配备了完善的释义) 注解列表如下 JPA注解 springMVC相关注解 全局异常处理 项目中具体配置解析和使用环境 使用注解的优势 采用纯java代码,不在需要配 ...
- 【SpringBoot 】SpringBoot注解详解
[SpringBoot ]SpringBoot注解详解 一.注解(annotations)列表 @SpringBootApplication:包含了@ComponentScan.@Configura ...
- java method 注解_JAVA 注解详解及简单实例
JAVA 注解详解及简单实例 何为注解 注解(Annotation)又称为元数据,在JDK1.5后引入,它的作用是: 生成文档 这是注解的原始用途,可以通过注解生成JavaDoc文档 跟踪代码的依赖 ...
最新文章
- mysql备份数据库命令
- Java 8:长期支持的堡垒
- java使用三种循环打印99表_编程题:利用for循环打印 9*9 表
- 安卓逆向_11 --- methodprofiling(方法分析)【在 smali 代码中打印信息 --- 协议分析常用】
- python with open 循环建立指定名字文件_Python基础——文件
- 自定义手势--输入法手势技术
- 由input type=file /获取的file.type为空字符串引申浏览器是如何获取文件的MIME类型...
- EndNoteX9完整详细安装教程
- 美了哭了,AduSkin是我见过最好看的WPF控件库
- “仿宋_GB2312、楷体_GB2312、方正小标宋简体”的下载和安装方式【实操】
- 三种方法求解Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1,当n比较大时,Fn也非常大,现在我们想知道,Fn除以10007的余数是多少。
- python的web框架flask_PythonWEB框架之Flask
- 《操作系统真象还原》——0.6 为什么称为“陷入”内核
- Anaconda介绍、安装及使用保姆级教程
- 【呆瓜学maven】Maven介绍(创建工程项目以及下载所需要的jar包)
- 服务器机柜与网络机柜
- ArcGIS10从入门到精通系列实验图文教程(附配套实验数据持续更新)
- 学区摇号软件设计_多校划片、电脑摇号之后,拼娃、拼钱、拼房的9种对应方案...
- 2021年中国纺织行业产业链发展分析:纺织行业下游零售市场逐渐恢复增涨[图]
- 历史上的今天:苹果电脑之父诞生;阿里巴巴收购雅虎中国;OpenAI 击败电竞世界冠军...
热门文章
- 树莓派书籍全方位推荐
- YUVPlayer: 基于Android平台的YUV视频原始数据播放器
- 《软件设计的哲学》读书总结
- 下载词云(wordcloud)
- 《羊了个羊》还在火!创始人被制成展牌,竟成母校招生“活广告”?
- mysql数据库d导出数据_mysql数据库导入导出
- Unity Android 加载Sprite
- VUE 项目图标全部替换成阿里巴巴矢量图
- 警告: 忽略额外的图例条目
- jq输出html 单引号引号转义符,jQuery.parseJSON由于JSON中的单引号转义而引发“无效JSON”错误...