Java 方法被截获两次,即使它被调用一次

Java 方法被截获两次,即使它被调用一次,java,bytecode,agent,byte-buddy,Java,Bytecode,Agent,Byte Buddy,在下面的代码片段中,我对子类的一个实例调用了一次方法doStuff。然而,它被截获了两次 请注意,doStuff是在父类超类中定义的。如果子类中定义了doStuff,则拦截逻辑将按预期工作:只有一个拦截 我是否错误地使用了Byte Buddy package com.test; import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.

在下面的代码片段中,我对
子类的一个实例调用了一次方法
doStuff
。然而,它被截获了两次

请注意,
doStuff
是在父类
超类中定义的。如果
子类
中定义了
doStuff
,则拦截逻辑将按预期工作:只有一个拦截

我是否错误地使用了Byte Buddy

package com.test;

import static net.bytebuddy.matcher.ElementMatchers.any;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;

import java.util.concurrent.Callable;

import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType.Builder;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;

import org.junit.Test;

public class ReproBugTest {

    @Test
    public void reproBug() {

        new AgentBuilder.Default().type(nameStartsWith("com.test"))
                                    .transform(new AgentBuilder.Transformer() {

                                        @Override
                                        public Builder<?> transform(
                                                Builder<?> builder,
                                                TypeDescription td) {

                                            return builder.method(any())
                                                            .intercept(
                                                                    MethodDelegation.to(MethodInterceptor.class));
                                        }
                                    })
                                    .installOn(
                                            ByteBuddyAgent.installOnOpenJDK());

        SubClass subClass = new SubClass();
        subClass.doStuff();
    }
}

class SuperClass {
    public void doStuff() {
        System.out.println("Doing stuff...");
    }
}

class SubClass extends SuperClass {
}

class MethodInterceptor {

    @RuntimeType
    public static Object intercept(@SuperCall Callable<?> zuper)
            throws Exception {

        // Intercepted twice, bug?
        System.out.println("Intercepted");

        Object returnValue = zuper.call();

        return returnValue;
    }
}
package.com.test;
导入静态net.bytebuddy.matcher.ElementMatchers.any;
导入静态net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
导入java.util.concurrent.Callable;
导入net.bytebuddy.agent.bytebuddy代理;
导入net.bytebuddy.agent.builder.AgentBuilder;
导入net.bytebuddy.description.type.TypeDescription;
导入net.bytebuddy.dynamic.DynamicType.Builder;
导入net.bytebuddy.implementation.MethodDelegation;
导入net.bytebuddy.implementation.bind.annotation.RuntimeType;
导入net.bytebuddy.implementation.bind.annotation.SuperCall;
导入org.junit.Test;
公开课考试{
@试验
公共图书馆{
new AgentBuilder.Default().type(nameStartsWith(“com.test”))
.transform(新的AgentBuilder.Transformer(){
@凌驾
公共建筑商转型(
建筑商,
字体说明(td){
返回builder.method(any())
.拦截(
MethodDelegation.to(MethodInterceptor.class));
}
})
.安顿(
installOnOpenJDK());
SubClass SubClass=新的SubClass();
子类doStuff();
}
}
类超类{
公共空间{
System.out.println(“做事…”);
}
}
类子类扩展了超类{
}
类方法拦截器{
@运行时类型
公共静态对象截获(@SuperCall Callable zuper)
抛出异常{
//截获了两次,巴格?
System.out.println(“拦截”);
对象returnValue=zuper.call();
返回值;
}
}

您正在拦截每种类型的方法调用,即
子类
超类
。您需要进一步指定要拦截的方法的拦截器。在您的例子中,您只希望拦截由给定类型声明的方法

这很容易实现。您应该拦截
builder.method(isDeclaredBy(td))
,而不是
builder.method(any())
。这样,仅当被截获类型声明方法时,才会截获该方法


最后,我可以从源代码中看出,您使用的是旧版本的Byte Buddy。版本0.7-rc6运行稳定,具有附加功能,并修复了几个bug。(但是,某些API仍然需要更改。)

此代码段在0.7-rc6上运行,但我可能仍在使用一些旧的API。哦,顺便说一句,0.7-rc6的在线javadoc似乎已经坏了:Byte BuddyAgent方法
ByteBuddyAgent.installOnOpenJDK()
应该是
ByteBuddyAgent.install()
,因为它支持J9和任何与Java 9兼容的平台。感谢对javadoc的提示,这是一个命名问题。现在修好了。