本章解释如何通过Advice代码更改函数代码的方法参数、方法返回和实例变量的值。
这是DataProducer.java的代码
public class DataProducer{
public static String CLASSNAME = "DataProducer";
public int recordId = 1;
private String descr = "someData";
public int create(int id, String data){
System.out.println("Parameter data:" + id + ", " + data);
System.out.println("Instance variable data:" + recordId + ", " + descr + ", " + CLASSNAME);
if(data != null)
return 1;
else
return -1;
}
}
DataProducer.java声明了一个名为create的函数方法,它接受两个参数。
create方法在屏幕上打印其参数值id、data以及recordId、descr和CLASSNAME实例变量的值。
之后,该方法根据数据参数的值返回 1 或 -1 的整数值。
当这些程序语句(位于Main1.java中)在没有Advice代码的情况下执行时。
public class Main1 {
public static void main(String[] args) {
int dataReceived = new DataProducer().create(10000, "test");
System.out.println("Data returned from create() : " + dataReceived);
}
}
程序在屏幕上生成此结果
Parameter data : 10000, test
Instance variable data : 1, someData, DataProducer
Data returned from create() : 1
现在执行maven构建过程,然后执行Main1.java,程序在屏幕上生成此结果
Parameter data : 20000, test2
Instance variable data : 2, newData, DataProducer1
Data returned from create() : -2
观察到屏幕上返回的数据已更改,即使是相同的程序Main1.java:
参数数据:“10000,test”更改为“20000,test2”
实例变量:“1,someData,DataProducer”已更改为“2,newData,DataProvider1”
从create方法返回的数据:“1”更改为“-2”.
发生数据更改是因为Advice代码在检测过程中截取了值。
这是DataInterceptor.java的代码,也是本次中的Advice代码:
public class DataInterceptor {
@Advice.OnMethodEnter
public static void methodStart(
@Advice.Argument(value=0, readOnly=false) int param1,
@Advice.Argument(value=1, readOnly=false, typing=Assigner.Typing.DYNAMIC) Object param2,
@Advice.FieldValue(value="recordId", readOnly=false) int data1,
@Advice.FieldValue(value="descr", readOnly=false, typing=Assigner.Typing.DYNAMIC) Object data2,
@Advice.FieldValue(value="CLASSNAME", readOnly=false, typing=Assigner.Typing.DYNAMIC) Object data3){
param1 = 20000;
param2 = "test2";
data1 = 2;
data2 = "newData";
data3 = "DataProducer1";
}
@Advice.OnMethodExit
public static void methodEnd(
@Advice.Return(readOnly=false) int returnObject){
returnObject = -2;
}
}
要截取参数、实例变量和返回对象的值,检测过程必须实现OnMethodEnter Advice、OnMethodExit Advice和plugin程序。
本章将重点解释Advice代码的实现,因为plugin程序与前一章类似。
1、截获方法参数值
更改DataProducer.java的create方法的id参数的值。
DataInterceptor.java对param1参数使用@Argument注释
@Advice.Argument(value=0, readOnly=false) int param1
value属性使用值0,这意味着该参数被映射到create方法的第一个参数。
注释为readOnly属性指定了false值,这意味着advice方法想要更改create方法的第一个参数的值。
readOnly属性的默认值为true,这将防止Advice代码更改参数值。
因此,Advice方法可以将第一个参数值从10000更改为20000:
param1 = 20000;
接下来,该注解被配置为更改create方法的第二个参数的值:
@Advice.Argument(value=1, readOnly=false, typing=Assigner.Typing.DYNAMIC) Object param2
value属性为1表示param2映射到create方法的第二个参数。
readOnly为false属性表示数据参数的值是可变的,param2参数映射到数据参数。
第三个属性type的值为Assigner.typing.DYNAMIC。
数据参数是java.lang.String,Advice方法尝试使用java.lang.Object映射param2参数。
为了使param2具有java.lang.String以外的其他数据类型,type属性的值必须为DYNAMIC(动态的),否则,maven构建过程将失败。
Advice注解的类型属性默认为static。
param2必须具有与其映射参数完全相同的数据类型,
因为param2想要使用java.lang.Object,所以DYNAMIC类型是正确的属性值。
因此,使用@Argument注解中的这些配置,Advice代码可以将param2的值从"test"更改为"test2":
param2 = "test2";
2、拦截实例和类变量
advice方法还更改DataProducer.java的实例变量和类变量的值。
有一个名为CLASSNAME的类变量和两个名为recordId和descr的实例变量。
要更改实例或类变量的值,请使用@FieldValue注解而不是@Argument。
此注解配置为更改recordId的值:
@Advice.FieldValue(value="recordId", readOnly=false) int data1
要将data1参数映射到recordId实例变量,value属性指定实例变量的名称"recordId"。
readOnly属性的值为false,这意味着recordId变量的值在advice方法中是可变的。
因此,将recordId变量的值从1更改为2
data1 = 2;
此注解配置为更改DataProducer.java中descr实例变量的值:
@Advice.FieldValue(value=”descr”,readOnly=false,
typing=Assigner.Typing.DYNAMIC) Object data2
该注解希望使用java.lang.Object数据类型来映射descr实例变量的数据类型,即java.langString。
因此,type属性必须使用Assigner.typing.DYNAMIC的值。
通过这些配置,将descr变量的值从"some data"更改为"newData":
data2 = "newData";
@FieldValue还可以更改函数代码的类变量。
为此配置此注释:
@Advice.FieldValue(value=”CLASSNAME”,readOnly=false,
typing=Assigner.Typing.DYNAMIC) Object data3
通过这些配置,将CLASSNAME变量的值从"DataProducer"更改为"DataProvider1":
data3 = "DataProducer1";
3、截取方法返回值
除了更改方法参数和实例变量的值之外,Advice代码还可以更改create方法的返回值。
为此,@Return注释必须用于OnMethodExit advice方法的参数:
@Advice.OnMethodExit
public static void methodEnd(@Advice.Return(readOnly=false) int returnObject){
returnObject = -2;
}
使用@Return注释,methodEnd方法中returnObject变量的值更改将反映在create方法的返回值中。
利用该配置,将返回值从1更改为-2:
returnObject = -2;
结论
本章解释:
如何更改函数方法的方法参数值
如何更改函数代码实例变量的值
如何更改函数方法的返回值
bytebuddy书籍《Java Interceptor Development with ByteBuddy: Fundamental》
喜欢就点个👍吧














网友评论