【问题标题】:How to use ByteBuddy delegation for intercepting bootstrap class method如何使用 ByteBuddy 委托拦截引导类方法
【发布时间】:2019-04-09 06:45:03
【问题描述】:

我正在使用 java 代理和 bytebuddy 来拦截 FileIOStreams 中的“读取”和“写入”方法。要实现的一个功能是“在某些情况下调用原始方法,否则通过”。因此,我需要使用方法委托来完全控制调用流程,而不是用 Advice 包装它。

当@Morph 不存在时方法拦截工作正常,但当我将@Morph 添加到参数时它不起作用。我已经用其他一些注释进行了测试:

添加@AllArguments,@This不会阻塞委托,该方法将作为我的拦截器运行;

添加@Morph,@SuperCall 将阻止委托。不会抛出异常:原始方法将像以前一样运行。

这是我要实现的代码:

public static void mountAgent(Instrumentation inst) {

        new AgentBuilder.Default()
                .with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
                .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
                .with(AgentBuilder.TypeStrategy.Default.REDEFINE)
                .ignore(new AgentBuilder.RawMatcher.ForElementMatchers(nameStartsWith("net.bytebuddy.").or(isSynthetic()), any(), any()))
                .with(new AgentBuilder.Listener.Filtering(
                        new StringMatcher("java.io.FileInputStream", StringMatcher.Mode.EQUALS_FULLY)
                                .or(new StringMatcher("java.io.FileOutputStream", StringMatcher.Mode.EQUALS_FULLY)),
                        AgentBuilder.Listener.StreamWriting.toSystemOut()))
                .type(named("java.io.FileOutputStream"))
                .transform(new AgentBuilder.Transformer() {
                    @Override
                    public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder,
                                                            TypeDescription typeDescription,
                                                            ClassLoader classLoader,
                                                            JavaModule module) {
                        return builder.method(named("write").and(not(isNative())).and(takesArgument(0, byte[].class)))
                                .intercept(MethodDelegation
                                        .withDefaultConfiguration()
                                        .withBinders(Morph.Binder.install(Morphing.class))
                                        .to(WriteInterceptor.class));
                    }})
                .installOn(inst);
    }

(跳过将拦截器附加到 BootstrapClassLoaderSearch 的代码)

以下是我的拦截器:

public interface Morphing<T> {
        T Object invoke(Object[] agrs);
    }

@SuppressWarnings("unused")
public static class WriteInterceptor {
    @RuntimeType
    public static void write(
//change the input here
            byte[] bytes,
            @AllArguments Object[] args,
            @Morph Morphing<Void> morphing
    ) throws Exception {
        if (true) {
            morphing.invoke(args);
        }
        else {
            // do something
            throw new Exception();
        }
    }
}

如果拦截函数的输入为空或只有byte[]字节,则委托会起作用并抛出异常:

[Byte Buddy] IGNORE java.io.FileInputStream [null, module java.base, loaded=true]
[Byte Buddy] COMPLETE java.io.FileInputStream [null, module java.base, loaded=true]
[Byte Buddy] DISCOVERY java.io.FileOutputStream [null, module java.base, loaded=true]
[Byte Buddy] TRANSFORM java.io.FileOutputStream [null, module java.base, loaded=true]
[Byte Buddy] COMPLETE java.io.FileOutputStream [null, module java.base, loaded=true]

Exception: java.lang.Exception thrown from the UncaughtExceptionHandler in thread "main"

如果输入是

byte[] 字节,@AllArguments Object[] args,@Morph Morphing 变形

@AllArguments Object[] 参数,@Morph Morphing 变形

调用内置的write函数,输出为

[Byte Buddy] IGNORE java.io.FileInputStream [null, module java.base, loaded=true]
[Byte Buddy] COMPLETE java.io.FileInputStream [null, module java.base, loaded=true]
[Byte Buddy] DISCOVERY java.io.FileOutputStream [null, module java.base, loaded=true]
[Byte Buddy] TRANSFORM java.io.FileOutputStream [null, module java.base, loaded=true]
[Byte Buddy] COMPLETE java.io.FileOutputStream [null, module java.base, loaded=true]

添加@Morph后委托不起作用,但是bytebuddy仍然说transform完成是什么原因?如何为这种情况获得正确的变形?谢谢!

【问题讨论】:

    标签: java instrumentation byte-buddy javaagents


    【解决方案1】:

    我假设您的重新转换已经失败。您是否尝试将侦听器添加到重新转换过程中。 Exception: java.lang.Exception throw from the UncaughtExceptionHandler in thread "main" 是什么意思?

    我注意到的一件事是您没有调整模块图。 java.base 模块将无法看到您的拦截器,该拦截器很可能加载在引导加载程序的未命名模块中。您是否尝试在指向拦截器类的转换中添加 assureReadEdgeTo

    另外,请注意Advice 确实允许您跳过甚至重复执行方法。查看进入或退出方法的 javadoc。通常,在检测引导类时,建议往往更可靠。

    【讨论】:

    • 感谢您的建议!我确实没有检查 Advice.OnMethodEnter 中的参数。我非常感谢您在 bytebuddy 上所做的工作!对像我这样对java字节码一无所知的人太友好了
    猜你喜欢
    • 2020-02-20
    • 2015-11-18
    • 1970-01-01
    • 1970-01-01
    • 2020-08-17
    • 2021-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多