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》

----END----

喜歡就點個??吧

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容