如何使用ByteBuddy的@Advice.AllArguments替换输入参数?

huangapple go评论76阅读模式
英文:

How to replace input arguments using ByteBuddy's @Advice.AllArguments?

问题

我正在使用ByteBuddy的@Advice来转换我的类,一切都运行正常,直到我尝试替换输入参数。

我有一个名为FooService的类,其中有一个join方法,它只是将两个字符串用空格连接起来。

public class FooService {
    public String join(String message, String message1) {
        return message + " " + message1;
    }
}

我还有另一个方法,它接受一个Object[] args数组输入,并更改数组中的元素。

public static ArgsProcessor argsProcessor = args -> {
    args[0] = args[0] + "-suffix";
    args[1] = "replaced";
};

我尝试了不同的方法来使用argsProcessor@Advice.OnMethodEnter方法中操作输入参数。对我来说,以下的advice实现几乎是等效的,应该都能工作,但令人费解的是只有Advice1能正常工作。

public static class Advice1 {
    @Advice.OnMethodEnter
    public static void onEnter(@Advice.AllArguments(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object[] args) {
        Object[] newArgs = Arrays.copyOf(args, args.length);
        ArgsProcessor argsProcessor = Demo.argsProcessor;
        argsProcessor.process(newArgs);
        args = newArgs;
    }
}

public static class Advice2 {
    @Advice.OnMethodEnter
    public static void onEnter(@Advice.AllArguments(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object[] args) {
        Object[] newArgs = new Object[args.length];
        ArgsProcessor argsProcessor = Demo.argsProcessor;
        argsProcessor.process(args);
        System.arraycopy(args, 0, newArgs, 0, args.length);
        args = newArgs;
    }
}

public static class Advice3 {
    @Advice.OnMethodEnter
    public static void onEnter(@Advice.AllArguments(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object[] args) {
        Object[] newArgs = Arrays.copyOf(args, args.length);
        try {
            ArgsProcessor argsProcessor = Demo.argsProcessor;
            argsProcessor.process(newArgs);
            args = newArgs;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

public static class Advice4 {
    @Advice.OnMethodEnter
    public static void onEnter(@Advice.AllArguments(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object[] args) {
        ArgsProcessor argsProcessor = Demo.argsProcessor;
        argsProcessor.process(args);
    }
}

public static class Advice5 {
    @Advice.OnMethodEnter
    public static void onEnter(@Advice.AllArguments(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object[] args) {
        ArgsProcessor argsProcessor = Demo.argsProcessor;
        argsProcessor.process(args);
        args = Arrays.copyOf(args, args.length);
    }
}

输出结果:

Advice1 a-suffix replaced
Advice2 a b
Advice3 a b
Advice4 a b
Advice5 a b

代码片段链接:https://gist.github.com/raptium/ab7830e5d7f7cba43bbd2c2a5c7b38e0

英文:

I am using ByteBuddy's @Advice to transform my classes and it works fine until I try to replace input arguments.

I have a FooService with a join method which just joins two strings with a space.

public class FooService {
    public String join(String message, String message1) {
        return message + " " + message1;
    }
}

And I have another method which takes a Object[] args array input and changes elements in the array.

    public static ArgsProcessor argsProcessor = args -> {
        args[0] = args[0] + "-suffix";
        args[1] = "replaced";
    };

I've tried different ways to use the argsProcessor to manipulate the input arguments in @Advice.OnMethodEnter method. For me, the following advice implementations are almost equivalent and should all work, somehow only Advice1 works.

    public static class Advice1 {
        @Advice.OnMethodEnter
        public static void onEnter(@Advice.AllArguments(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object[] args) {
            Object[] newArgs = Arrays.copyOf(args, args.length);
            ArgsProcessor argsProcessor = Demo.argsProcessor;
            argsProcessor.process(newArgs);
            args = newArgs;
        }
    }

    public static class Advice2 {
        @Advice.OnMethodEnter
        public static void onEnter(@Advice.AllArguments(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object[] args) {
            Object[] newArgs = new Object[args.length];
            ArgsProcessor argsProcessor = Demo.argsProcessor;
            argsProcessor.process(args);
            System.arraycopy(args, 0, newArgs, 0, args.length);
            args = newArgs;
        }
    }

    public static class Advice3 {
        @Advice.OnMethodEnter
        public static void onEnter(@Advice.AllArguments(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object[] args) {
            Object[] newArgs = Arrays.copyOf(args, args.length);
            try {
                ArgsProcessor argsProcessor = Demo.argsProcessor;
                argsProcessor.process(newArgs);
                args = newArgs;
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public static class Advice4 {
        @Advice.OnMethodEnter
        public static void onEnter(@Advice.AllArguments(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object[] args) {
            ArgsProcessor argsProcessor = Demo.argsProcessor;
            argsProcessor.process(args);
        }
    }

    public static class Advice5 {
        @Advice.OnMethodEnter
        public static void onEnter(@Advice.AllArguments(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object[] args) {
            ArgsProcessor argsProcessor = Demo.argsProcessor;
            argsProcessor.process(args);
            args = Arrays.copyOf(args, args.length);
        }
    }

Output

Advice1 a-suffix replaced
Advice2 a b
Advice3 a b
Advice4 a b
Advice5 a b

The code snippet https://gist.github.com/raptium/ab7830e5d7f7cba43bbd2c2a5c7b38e0

答案1

得分: 1

运行您的代码,我得到以下结果:

Advice1 a-suffix替换
Advice2 a b
Advice3 a-suffix替换
Advice4 a b
Advice5 a b

这正是我期望的结果。Byte Buddy将advice方法用作模板。此代码实际上并未被执行。当您在方法中读取args时,Byte Buddy 每次都会创建一个新数组

因此,计算args == args会返回false,因为Byte Buddy每次都会创建一个包含所有参数的新数组!如果您更改了args数组,您必须将其写回以便Byte Buddy能够发现相应的字节码,并将其映射回赋值。

英文:

Running your code, I get

Advice1 a-suffix replaced 
Advice2 a b 
Advice3 a-suffix replaced 
Advice4 a b 
Advice5 a b

what is what I expect. Byte Buddy uses advice methods as templates. This code is not really exeucted. When you read args in your method, Byte Buddy creates a new array every time.

Therefore, computing args == args would return false since Byte Buddy creates a new array containing all arguments every time! If you change the args array, you have to write it back for Byte Buddy to discover the corresponding byte code and to map it back to an assignment.

huangapple
  • 本文由 发表于 2020年5月29日 10:25:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/62077684.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定