本章解释如何通过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》
喜欢就点个吧