代码之家  ›  专栏  ›  技术社区  ›  ddimitrov

Bytebuddy代理应用通知时出现“重复类”错误

  •  0
  • ddimitrov  · 技术社区  · 6 年前

    我有一些基于ByteBuddy的工具,我想为嵌入式和代理提供它们。

    代码如下:

    public static void premain(String arguments, Instrumentation instrumentation) {
        installedInPremain = true;
        new AgentBuilder.Default()
                .type(ElementMatchers.named("com.acme.FooBar"))
                .transform((builder, typeDescription, classLoader, module) -> visit(builder))
                .installOn(instrumentation);
    
    }
    
    public static void instrumentOnDemand() {
        ByteBuddyAgent.install();
        DynamicType.Builder<URLPropertyLoader> typeBuilder = new ByteBuddy().redefine(FooBar.class);
        DynamicType.Builder<FooBar> visited = visit(typeBuilder);
        visited.make().load(FooBar.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
    }
    
    private static <T> DynamicType.Builder<T> visit(DynamicType.Builder<T> builder) {
        return builder.visit(Advice.to(SnoopLoad.class).on(named("load").and(takesArguments(0))))
                    .visit(Advice.to(SnoopOpenStream.class).on(named("openStream")))
                    .visit(Advice.to(SnoopPut.class).on(named("put")));
    }
    

    然后在别的地方,有人会做:

    new FooBar().load("abc123")
    

    如果我和 instrumentOnDemand ,一切都会好起来的,但是如果我和premain一起管理代理,我会得到:

    Exception in thread "main" java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for name: "com/acme/FooBar"
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        at com.acme.FooBarAnalyzer.load(FooBarAnalyzer.java:121)
        at com.acme.FooBarAnalyzer.main(FooBarAnalyzer.java:107)
    

    我想是因为我的建议 FooBar 作为 @Advice.This 参数,它会过早加载,但不是让代理重新定义这些参数的全部意义吗?

    另外,在点播的情况下,它是如何工作的?我想我需要调整代理生成器,还是我漏了一步?

    啊。。。不理解就照搬教程的危险。。。文档指针也非常受欢迎!

    2 回复  |  直到 6 年前
        1
  •  1
  •   Rafael Winterhalter    6 年前

    你可以使用 AgentBuilder.Transformer.ForAdvice 用于启动和动态检测。这通常是一个更好的选择,因为这对于不同的类装入器星座也更为健壮。这样你就不需要重复你的逻辑了。

        2
  •  0
  •   ddimitrov    6 年前

    好吧,解决办法是 AgentBuilder.Transformer.ForAdvice() ,但这造成了 premain visit .

    public static void premain(String arguments, Instrumentation instrumentation) {
        installedInPremain = true;
        new AgentBuilder.Default()
                .type(named("com.acme.FooBar"))
    //          .transform((builder, typeDescription, classLoader, module) -> visit(builder))
    
                 // CAN'T WE HAVE THIS SHARED???
                .transform(advice("load",           "SnoopLoad"))
                .transform(advice("openStream",     "SnoopOpenStream"))
                .transform(advice("put",            "SnoopPut"))
                .installOn(instrumentation);
    
    }
    
    private static AgentBuilder.Transformer.ForAdvice advice(String name, String snoopLoad) {
        return new AgentBuilder.Transformer.ForAdvice().advice(named(load), "com.acme.PropAnalyzerAgent$" + snoopLoad);
    }
    
    private static <T> DynamicType.Builder<T> visit(DynamicType.Builder<T> builder) {
        // CAN'T WE HAVE THIS SHARED???
        return builder.visit(Advice.to(SnoopLoad.class).on(named("load")))
                    .visit(Advice.to(SnoopOpenStream.class).on(named("openStream")))
                    .visit(Advice.to(SnoopPut.class).on(named("put")));
    }
    
    
    public static void instrumentOnDemand() {
        DynamicType.Builder<FooBar> typeBuilder = new ByteBuddy().redefine(FooBar.class);
        DynamicType.Builder<FooBar> visited = visit(typeBuilder);
        visited.make().load(FooBar.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
    }
    

    没有什么是不能通过一层间接解决的,但我想它已经以更好的方式处理了。