ByteBuddy(五)—拦截方法参数、方法返回和实例变量

本章解释如何通过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方法在屏幕上打印其参数值iddata以及recordIddescrCLASSNAME实例变量的值。
之后,该方法根据数据参数的值返回 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.javacreate方法的id参数的值。
DataInterceptor.javaparam1参数使用@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方法的第二个参数
readOnlyfalse属性表示数据参数的值是可变的,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的类变量和两个名为recordIddescr的实例变量。

要更改实例或类变量的值,请使用@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.javadescr实例变量的值:

@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》

喜欢就点个吧

你可能感兴趣的:(ByteBuddy(五)—拦截方法参数、方法返回和实例变量)