java - 拦截来自特定命名空间的所有方法/构造函数/getters/setters

标签 java code-generation bytecode instrumentation byte-buddy

我有一个像这样实现的 Java 代理:

public static void premain(String args, Instrumentation instrumentation) throws ClassNotFoundException {
    new AgentBuilder.Default()
      .type(isSubTypeOf(Object.class).and(nameStartsWith("com.my.domain")))
      .transform(new Transformer5())
      .installOn(instrumentation);
}

然后是变换类:

public class Transformer5 implements AgentBuilder.Transformer {
    public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader) {
        return builder.method(any().and(isDeclaredBy(typeDescription)))
                      .intercept(MethodDelegation.to(Interc4.class));
    }
}

和拦截器:

public class Interc4 {

    static String indent = "";

    @RuntimeType
    @BindingPriority(BindingPriority.DEFAULT * 3)
    public static Object intercept(@SuperCall Callable<?> zuper, 
                                   @AllArguments Object[] allArguments, 
                                   @Origin String method) throws Exception {

        System.out.println(indent + "Intercepted4 M" + method);

        try {
            indent += "  ";
            return zuper.call();
        } finally {
            //post process
            indent = indent.substring(0, indent.length()-2);
        }

    }

}

这样做的问题是它不会拦截构造函数,并且还会给出此类错误

Cannot define non-public or non-virtual method 'lambda$static$1' for interface type

制作拦截器的最佳方法是什么,该拦截器将代理来自某个域的类中的每个方法(我希望能够获取方法名称,检查方法参数(如果有)并转发执行)。

最佳答案

发生这种情况有两个原因。

  1. 无法为接口(interface)类型定义非公共(public)或非虚拟方法“lambda$static$1”是由 Byte Buddy 中的验证错误引起的。我将在下一个版本中修复此问题。同时,您可以通过 new AgentBuilder.Default(new ByteBuddy().with(TypeValidation.DISABLED)) 禁用验证。

  2. 您仅通过 .method( ... ) 匹配来显式拦截方法。您可以通过 .constructor( ... ) 拦截构造函数,或通过 .invokable( ... ) 拦截任何可调用对象。但是,您将无法将拦截器用于必须对 super 方法调用进行硬编码的构造函数。但是,您可以将拦截器分成两部分:

    class Interceptor {
      public static void before() { ... }
      public static void after() { ... }
    }
    

    使用

    .intercept(MethodDelegation.to(Interceptor.class).filter(named("before")
       .andThen(SuperMethodCall.INSTANCE
       .andThen(MethodDelegation.to(Interceptor.class).filter(named("after"))))
    

这样,Byte Buddy 就可以对 super 构造函数调用进行硬编码,同时触发 beforeafter 方法。请注意,@This 注释对于 before 方法不可用。

关于java - 拦截来自特定命名空间的所有方法/构造函数/getters/setters,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38523948/

相关文章:

php - 动态创建类

c - 用字节码表示数组和记录

java - 最好的选择?编译前编辑字节码(asm)或编辑java文件

java - 方法被拦截两次,即使它被调用一次

java - Java 中的关联数组

java - JAVA中使用信号量控制线程访问

java - 如何在外部tomcat中部署Spring-boot REST API

java - 将 PSS 参数中的 "salt length"参数更改为 BouncycaSTLe

java - 生成Getter时如何忽略注解?

android - 从模板生成 C++、Java、XML 的工具