it-swarm.com.ru

Как загрузить файл JAR во время выполнения

Меня попросили создать систему Java, которая будет иметь возможность загружать новый код (расширения) во время работы. Как я могу повторно загрузить файл JAR, когда мой код работает? или как мне загрузить новую банку?

Очевидно, что поскольку постоянное время работы важно, я бы хотел добавить возможность перезагружать существующие классы во время его работы (если это не слишком усложняет).

Какие вещи я должен высматривать? (думайте об этом как о двух разных вопросах - один о перезагрузке классов во время выполнения, другой о добавлении новых классов).

72
Amir Arad

Перезагрузка существующих классов с существующими данными может привести к поломке.

Вы можете относительно легко загрузить новый код в загрузчики новых классов:

ClassLoader loader = URLClassLoader.newInstance(
    new URL[] { yourURL },
    getClass().getClassLoader()
);
Class<?> clazz = Class.forName("mypackage.MyClass", true, loader);
Class<? extends Runnable> runClass = clazz.asSubclass(Runnable.class);
// Avoid Class.newInstance, for it is evil.
Constructor<? extends Runnable> ctor = runClass.getConstructor();
Runnable doRun = ctor.newInstance();
doRun.run();

Загрузчики классов, которые больше не используются, могут быть собраны сборщиком мусора (если не происходит утечка памяти, как это часто бывает при использовании ThreadLocal, драйверов JDBC, Java.beans и т.д.).

Если вы хотите сохранить данные объекта, то я предлагаю механизм персистентности, такой как сериализация, или что вы привыкли.

Конечно, системы отладки могут делать более причудливые вещи, но они более хакерские и менее надежные.

Можно добавить новые классы в загрузчик классов. Например, используя URLClassLoader.addURL. Однако, если класс не загружается (например, вы его не добавили), он никогда не загрузится в этот экземпляр загрузчика классов.

79
Tom Hawtin - tackline

Это работает для меня:

File file  = new File("c:\\myjar.jar");

URL url = file.toURL();  
URL[] urls = new URL[]{url};

ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass("com.mypackage.myclass");
34
punkers

Меня попросили создать систему Java, которая будет иметь возможность загружать новый код во время работы

Возможно, вы захотите основать свою систему на OSGi (или, по крайней мере, много на это), что было сделано именно для этой ситуации.

Работать с загрузчиками классов действительно сложно, в основном из-за того, как работает видимость классов, и вы не хотите столкнуться с трудными для отладки проблемами позже. Например, Class.forName () , который широко используется во многих библиотеках, не слишком хорошо работает на фрагментированном пространстве загрузчика классов.

8
Thilo

Я немного погуглил и нашел этот код здесь :

File file = getJarFileToLoadFrom();   
String lcStr = getNameOfClassToLoad();   
URL jarfile = new URL("jar", "","file:" + file.getAbsolutePath()+"!/");    
URLClassLoader cl = URLClassLoader.newInstance(new URL[] {jarfile });   
Class loadedClass = cl.loadClass(lcStr);   

Кто-нибудь может поделиться мнениями/комментариями/ответами относительно этого подхода?

3
Amir Arad

Используйте org.openide.util.Lookup и ClassLoader для динамической загрузки плагина Jar, как показано здесь.

public LoadEngine() {
    Lookup ocrengineLookup;
    Collection<OCREngine> ocrengines;
    Template ocrengineTemplate;
    Result ocrengineResults;
    try {
        //ocrengineLookup = Lookup.getDefault(); this only load OCREngine in classpath of  application
        ocrengineLookup = Lookups.metaInfServices(getClassLoaderForExtraModule());//this load the OCREngine in the extra module as well
        ocrengineTemplate = new Template(OCREngine.class);
        ocrengineResults = ocrengineLookup.lookup(ocrengineTemplate); 
        ocrengines = ocrengineResults.allInstances();//all OCREngines must implement the defined interface in OCREngine. Reference to guideline of implement org.openide.util.Lookup for more information

    } catch (Exception ex) {
    }
}

public ClassLoader getClassLoaderForExtraModule() throws IOException {

    List<URL> urls = new ArrayList<URL>(5);
    //foreach( filepath: external file *.JAR) with each external file *.JAR, do as follows
    File jar = new File(filepath);
    JarFile jf = new JarFile(jar);
    urls.add(jar.toURI().toURL());
    Manifest mf = jf.getManifest(); // If the jar has a class-path in it's manifest add it's entries
    if (mf
            != null) {
        String cp =
                mf.getMainAttributes().getValue("class-path");
        if (cp
                != null) {
            for (String cpe : cp.split("\\s+")) {
                File lib =
                        new File(jar.getParentFile(), cpe);
                urls.add(lib.toURI().toURL());
            }
        }
    }
    ClassLoader cl = ClassLoader.getSystemClassLoader();
    if (urls.size() > 0) {
        cl = new URLClassLoader(urls.toArray(new URL[urls.size()]), ClassLoader.getSystemClassLoader());
    }
    return cl;
}
2
Doan Huynh