cast across classloader?

走远了吗. 提交于 2020-01-08 17:14:26

问题


How can I do this:

class Foo {
  public static Foo get() throws Exception {
    ClassLoader cl = new URLClassLoader(new URL[]{"foo.jar"}, null); // Foo.class is in foo.jar
    return (Foo)cl.loadClass("Foo").newInstance(); // fails on class cast
  }
}

What I need is for the JVM to consider the Foo instance from cl as if it is an instance of Foo from the classloader of the executing code.

I have seen these approaches, none of them good for me (the above example is a toy example):

  1. Load the class (or a separate interface) by a class loader that is a parent of both the calling code and created classloader
  2. Serialize and deserialize the object.

回答1:


Not possible. Class identity consists of the fully qualified name and the class loader.

Casting an object to a class with the same name loaded by different classloaders is no different than trying to cast a String to Integer, because those classes really could be completely different despite having the same name.




回答2:


I just spent the last two days struggling with this exact issue and I finally got around the problem by using java reflection:

// 'source' is from another classloader
final Object source = events[0].getSource();

if (source.getClass().getName().equals("org.eclipse.wst.jsdt.debug.internal.core.model.JavaScriptThread")) {

    // I cannot cast to 'org.eclipse.wst.jsdt.debug.internal.core.model.JavaScriptThread'
    // so I invoke the method 'terminate()' manually
    Method method = source.getClass().getMethod("terminate", new Class[] {});
    method.invoke(source, new Object[] {});
}

Hope this helps someone.




回答3:


If class which need be cast implements Serializable then:

private <T> T castObj(Object o) throws IOException, ClassNotFoundException {
    if (o != null) {
        ByteArrayOutputStream baous = new ByteArrayOutputStream();
        {
            ObjectOutputStream oos = new ObjectOutputStream(baous);
            try {
                oos.writeObject(o);
            } finally {
                try {
                    oos.close();
                } catch (Exception e) {
                }
            }
        }

        byte[] bb = baous.toByteArray();
        if (bb != null && bb.length > 0) {
            ByteArrayInputStream bais = new ByteArrayInputStream(bb);
            ObjectInputStream ois = new ObjectInputStream(bais);
            T res = (T) ois.readObject();
            return res;
        }
    }
    return null;
}

usage:

Object o1; // MyObj from different class loader
MyObj o2 = castObj(o1);



回答4:


Perhaps something using interfaces and java.lang.reflect.Proxy would suit your preferences. Using an InvocationHandler that finds and invokes the relevant method on the target class. (Note, any mobile-code security you have will be shot through if you do this.)




回答5:


No possible to cast in different classLoader.

You have this workaround with Gson, example cast Object to YourObject (Object is a YourObject class but in other classLoader):

Object o = ... 
Gson gson = new Gson();
YourObject yo = gson.fromJson(gson.toJson(o), YourObject.class);

I use this workaround because I compile any java code in a WebApp (on Tomcat). This workaround run in production.




回答6:


This is an old post where I arrived because I wanted to do nearly the same thing, but a simpler version of it...

It actually works if both Foo and the loaded class (in my case from a .java classfile in another package) extend the same class, say, AbstractTestClass.

Pieces of code:

public AbstractTestClass load(String toLoad) {
    try{
        Class test = Class.forName("testsFolder.testLoadable");
        Constructor ctorlist[] = test.getDeclaredConstructors();
        for(Constructor aConstructor : ctorlist){
           if(...){// find the good constructor
              Object loadedTest = aConstructor.newInstance(new Object[]{/*params*/});
              return (AbstractTestClass) test;
           }
        }
    }catch(...){}
    return new defaultTestClass();
}

This way I can insert the loaded class in an ArrayList<AbstractTestClass>.



来源:https://stackoverflow.com/questions/2591779/cast-across-classloader

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!