Java 转换类加载器?
我如何做到这一点:Java 转换类加载器?,java,classloader,Java,Classloader,我如何做到这一点: 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 } } 我需要JVM
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
}
}
我需要JVM从CL中考虑FO实例,就像它是执行代码的类加载器中的FO实例一样。p> 我见过这些方法,但没有一种对我有好处(上面的示例是一个玩具示例):
不可能。类标识由完全限定名和类加载器组成
将对象强制转换为由不同类加载器加载的具有相同名称的类与尝试将
字符串强制转换为整数无异,因为尽管名称相同,但这些类实际上可能完全不同。也许使用接口和java.lang.reflect.Proxy的东西会适合您的偏好。使用InvocationHandler
,查找并调用目标类上的相关方法。(请注意,如果您这样做,您所有的移动代码安全性都会被破解。)我刚刚花了两天的时间来解决这个问题,最后通过使用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[] {});
}
希望这对某人有所帮助。这是一篇我来的老帖子,因为我想做几乎相同的事情,但更简单的版本
如果Foo和加载的类(在我的例子中是另一个包中的.java类文件)都扩展同一个类,比如AbstractTestClass,那么它实际上是有效的
代码片段:
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();
}
通过这种方式,我可以将加载的类插入到数组列表中
不可能在不同的类加载器中强制转换。
对于Gson,您有一个解决方法,例如将对象强制转换为YourObject(对象是YourObject类,但在其他类加载器中):
我之所以使用这种变通方法,是因为我在WebApp(在Tomcat上)中编译任何java代码。此解决方案在生产中运行。如果需要强制转换的类实现了可序列化,则:
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;
}
因此,如果我们甚至不能铸造这些新实例,我们如何利用它们呢?如果我们所能做的就是objectobj=cl.loadClass(“Foo”).newInstance()代码>,那么我们如何调用Foo的新实例的方法?@Pacerier:嗯,你可以使用反射。但更实际的情况是让类从委托层次结构中的父类加载器扩展类或实现接口,这些类也可用于代码的其余部分。但是在示例代码中,没有父类加载器(构造函数的第二个参数为null)…第二个选项的性能会比使用反射更快吗?或者它确实在内部使用反射吗?@Pacerier:第二个选项的工作原理与来自同一类加载器的类完全相同,因此它比使用反射更快;但随着JVM的每一个新版本,这个差距都在缩小。我们怎么能不经过反思就做到这一点呢?如果我们的类加载器加载的每个对象的每个方法都需要通过反射来调用,这不会严重阻碍整个程序吗?您从同一个类加载器加载“测试”。此外,即使您指定了另一个,它也必须是当前类装入器的子类,否则您将无法强制转换为AbstractTestClass@IttayD没错,它需要扩展父类(问题中的Foo)。这不是问题的要点:“从CL中考虑FO实例,就好像它是FoO的一个实例”?我只想指出,使用序列化等同于对象之间的深度复制字段。但是,它不会“复制”方法体。因此,主类加载器加载的MyObj(A)和从自定义加载器检索的MyObj(B)是不同的,只是大多数字段内容匹配。它在我部署动态web服务时帮助我解决了很多问题……谢谢@witThat,太棒了!这可能是一个黑客攻击,但它解决了一个非常恼人的部署问题。
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;
}
Object o1; // MyObj from different class loader
MyObj o2 = castObj(o1);