java 跨类加载器投射?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2591779/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 21:56:53  来源:igfitidea点击:

cast across classloader?

javaclassloader

提问by IttayD

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.

我需要让 JVM 考虑来自 cl 的 Foo 实例,就好像它是来自执行代码的类加载器的 Foo 实例一样。

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. 由作为调用代码和创建的类加载器的父级的类加载器加载类(或单独的接口)
  2. 序列化和反序列化对象。

回答by Michael Borgwardt

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 Stringto Integer, because those classes really could be completely different despite having the same name.

将对象强制转换为由不同类加载器加载的同名类与尝试将对象强制转换为Stringto没有什么不同Integer,因为尽管名称相同,但这些类实际上可能完全不同。

回答by wit

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);

回答by viragoboy

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

我刚刚花了两天时间来解决这个确切的问题,我终于通过使用 java 反射解决了这个问题:

// '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.

希望这可以帮助某人。

回答by Tom Hawtin - tackline

Perhaps something using interfaces and java.lang.reflect.Proxywould suit your preferences. Using an InvocationHandlerthat 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.)

也许使用界面的东西java.lang.reflect.Proxy会适合您的喜好。使用InvocationHandler在目标类上查找并调用相关方法的 。(请注意,如果您这样做,您拥有的任何移动代码安全性都会被击穿。)

回答by sgrillon

No possible to cast in different classLoader.

无法在不同的 classLoader 中进行转换。

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

您可以使用 Gson解决此问题,例如将 Object 强制转换为 YourObject(Object 是一个 YourObject 类,但在其他 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.

我使用此解决方法是因为我在 WebApp(在 Tomcat 上)中编译了任何 Java 代码。此解决方法在生产中运行。

回答by Aname

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.

如果 Foo 和加载的类(在我的例子中来自另一个包中的 .java 类文件)扩展相同的类,例如 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>.

这样我就可以将加载的类插入到ArrayList<AbstractTestClass>.