Бросать через загрузчик классов?
Как это сделать:
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 рассмотрел экземпляр Foo из cl, как если бы он был экземпляром Foo из загрузчика классов исполняемого кода.
Я видел эти подходы, ни один из них не хорош для меня (приведенный пример - пример игрушки):
- Загрузите класс (или отдельный интерфейс) загрузчиком классов, который является родителем как вызывающего кода, так и созданного загрузчика классов
- Сериализовать и десериализовать объект.
Ответы
Ответ 1
Невозможно. Идентификатор класса состоит из полного имени и загрузчика классов.
Передача объекта классу с тем же именем, который загружается разными загрузчиками классов, ничем не отличается от того, как использовать String
to Integer
, потому что эти классы действительно могут быть совершенно разными, несмотря на одно и то же имя.
Ответ 2
Я только что провел последние два дня, борясь с этой точной проблемой, и я, наконец, обошел эту проблему, используя 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[] {});
}
Надеюсь, это поможет кому-то.
Ответ 3
Если класс, который необходимо выполнить, реализует Serializable, то:
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);
Ответ 4
Возможно, что-то использование интерфейсов и java.lang.reflect.Proxy
будет соответствовать вашим предпочтениям. Использование InvocationHandler
, которое находит и вызывает соответствующий метод в целевом классе. (Обратите внимание, что любая защита вашего мобильного кода будет снята, если вы сделаете это.)
Ответ 5
Невозможно включить в класс classLoader.
У вас есть этот обходной путь с Gson, например, листинг Object to YourObject (Object - это класс YourObject, но в другом классеLoader):
Object o = ...
Gson gson = new Gson();
YourObject yo = gson.fromJson(gson.toJson(o), YourObject.class);
Я использую это обходное решение, потому что компилирую любой Java-код в WebApp (на Tomcat). Это обходное решение выполняется в процессе производства.
Ответ 6
Это старое сообщение, в котором я приехал, потому что хотел сделать почти то же самое, но более простая версия...
Это действительно работает, если оба Foo и загруженный класс (в моем случае из .java classfile в другом пакете) расширяют один и тот же класс, скажем, 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();
}
Таким образом, я могу вставить загруженный класс в ArrayList<AbstractTestClass>
.