如何以正确的方式加载此类?

时间:2018-03-12 10:12:21

标签: java classloader noclassdeffounderror bytecode byte-buddy

好吧,所以我有一个班级

class A{
    public D d = new B$0();

    public void foo(){
        B$0 b;
        try{
            b = (B$0)this.d;
        }catch(ClassCastException e){
             this.d = d.move(this); //actual implementation uses a CAS to make sure it's only replaced once
             throw new RepeatThisMethodException();
        }
        //do something with b here
    }
}

RepeatThisMethodException由某些代码进一步处理。

abstract class D{
    public abstract D move(Object o);
}

class B$0 extends D{
    public static D moveThis(A a){
        throw new Error();
    }

    public D move(Object o){
        return moveThis((A)o);
    }|
}

我现在创建一个新类

class B$1 extends D{
    public D move(Object o){
        return B$0.moveThis((A)o);
    }
}

使用ByteBuddy加载它。

    DynamicType.Builder builder = byteBuddy
            .subclass(D.class)
            .name("B$1")
            ;

    DynamicType.Unloaded newClass = builder.make();
    byte[] rawBytecode = newClass.getBytes();
    byte[] finishedBytecode = MyASMVisitor.addMethods(rawBytecode);

    Class b0 = Class.forName("B$0");
    ClassLoadingStrategy.Default.INJECTION.load(b0.getClassLoader(),
            Collections.singletonMap(newClass.getTypeDescription(), finishedBytecode));

(请注意,我使用B$0.class.getClassloader()加载B$1。)

move方法MyASMVisitor添加的字节码如下所示:

public Method move:"(Ljava/lang/Object;)LD;"
    stack 1 locals 2
{
        aload_1;
        checkcast   class A;
        invokestatic    Method B$0.moveThis:"(LA;)LD;";
        areturn;
}

现在加载了B$1,我重新设法B$0 s.t.它可以处理新的类。

class B$0 extends D{
    public static D moveThis(A a){
        if(a.d instanceof B$1) throw new RepeatThisMethodException();
        if(a.d instanceof B$0) return new B$1();
        throw new Error();
    }

    public D move(Object o){
        return moveThis((A)o);
    }|
}

并使用

重新加载
private void redefineClass(String classname, byte[] bytecode) {
    Class clazz;
    try{
        clazz = Class.forName(classname);
    }catch(ClassNotFoundException e){
        throw new RuntimeException(e);
    }

    ClassReloadingStrategy s = ClassReloadingStrategy.fromInstalledAgent();
    s.load(clazz.getClassLoader(),
            Collections.singletonMap((TypeDescription)new TypeDescription.ForLoadedType(clazz), bytecode));
}

因此B$0重新加载B$0.class.getClassLoader()

现在B$1存在且可以处理,我让A知道它应该从现在开始使用新类。

class A{
    public D d = new B$1();

    public void foo(){
        B$1 b;
        try{
            b = (B$1)this.d;
        }catch(ClassCastException e){
             this.d = d.move(this); //actual implementation uses a CAS to make sure it's only replaced once
             throw new RepeatThisMethodException();
        }
        //do something with b here
    }
}

使用相同的redefineClass方法重新加载它(因此A会重新加载A.class.getClassLoader())。

实际上,A的新实例将从一开始就使用B$1,而现有实例将调用b.move(this),而B$0.moveThis((A)o)将调用this(它无法使用B

目前这似乎有效。

现在的问题是我们需要更新所有使用G版本的类,显然,我们不能同时重新加载它们,所以有些会更早,有些会迟到。 / p>

我们假设我们有一个使用A a的班级a.d,因此使用A

G已经重新加载,A还没有。因此A(或move的任何其他已重新加载的客户端)上的某些方法可能已触发GB$0仍在尝试转换为G

没关系。

如果A a使用a.d并且无法将a.d.move(a)投射到其预期的版本,则会调用B$0.moveThis((A)a),然后调用if(a.d instanceof B$1) throw new RepeatThisMethodException();

在那种情况下,

B$0
我们在G中处理代码中的

可确保B$1在重新加载字节码并且知道B$1之前无法取得进展。

WOULD ,如果B$0.moveThis可以致电Exception in thread "MyT1" java.lang.NoClassDefFoundError: A at B$1.move(Unknown Source)

相反,我们得到了

Object o

好吧,这很不幸。让我们看看我们是否可以通过将B$0.moveThis转换为Exception in thread "MyT1" java.lang.NoClassDefFoundError: B$0 at B$1.move(Unknown Source) 来摆脱此错误...

B$1

不,看起来不像。

如何加载B$0 s.t.它至少可以访问B$0AA(以及最终D :> B :> C的任何客户)都可以访问它吗?

注意

任何类型的解决方案都需要支持向上转播。

E.g。说我有B b = new C()我们使用C(或将B的实例传递给期望b.move(b)或...)的方法,然后C$0.moveThis((C)b)必须仍然致电 Class b0 = Class.forName("B$0"); ClassLoadingStrategy.Default.INJECTION.load(b0.getClassLoader(), Collections.singletonMap(newClass.getTypeDescription(), finishedBytecode)); try { Class c = Class.forName("B$1"); Object instance = c.newInstance(); c.getMethod("move", Object.class).invoke(instance, new Object()); } catch (Exception e) { e.printStackTrace(); }

更新(谢谢,Holger)

这些问题似乎与重新定义现有类无关。

B$1.move()
在重新加载任何其他类之前调用​​NoClassDefFoundError

实际上足以触发clazz.getClassLoader()

更新

当我打印b0.getClassLoader()用于重新加载类而sun.misc.Launcher$AppClassLoader用于新类时,我总是得到<section class="s_features"> <center> <h4 style="padding-top: 1%;padding-bottom: 1%;font-family:timesnewroman;">PRODUCTS</h4> </center> <div class="container hidden-xs" id="moto" style=""> <div class="container"> <div class="row"> <div class="col-md-2"> <a id="bgc" href="/shop/category/light-bulb-4" class="btn btn-default"> <img class="img img-responsive padding-xl" src="/web/image/426/2543.png" alt="Odoo image and text block" data-original-title="" title="" style=""> <span style="font-family:timesnewroman;">BULB</span> </a> <br> </div> <div class="col-md-2"> <a id="bgc5" href="/shop/category/light-tube-light-9" class="btn btn-default"> <img class="img img-responsive center-block padding-large" src="/web/image/707/png.png" alt="Odoo image and text block" data-original-title="" title="" style=""> <span style="font-family:timesnewroman;">TUBE LIGHT</span> </a> <br> </div> </div> </div></div></section> <div class="col-md-9" id="products_grid"> <table id="tabdiv"> ---here the product images are displayed when I click 'a' tag </table> </div> 的相同实例。

1 个答案:

答案 0 :(得分:0)

这在related GitHub issue中讨论的实际代码生成中是一个问题。