Java - не самый динамичный язык, но при тщательном планировании и гибкости вы можете сделать свои программы немного более динамичными, загрузив классы во время выполнения.
Это может быть полезно, когда вы хотите сделать свое приложение более расширяемым и разрешить замену определенных модулей в нем, просто отбросив jar, реализующий интерфейс, в каталог.
Или вы можете загружать пользовательские плагины во время выполнения. Например, это может быть полезно для добавления функций в веб-приложение. Добавление подключаемого модуля может позволить вам изменить способ выполнения аутентификации пользователя или улучшить ведение журнала.
Код
Вот код, который поможет вам динамически загружать новый класс по пути к его jar-файлу.
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class ExtensionLoader<C> {
public C LoadClass(String directory, String classpath, Class<C> parentClass) throws ClassNotFoundException {
File pluginsDir = new File(System.getProperty("user.dir") + directory);
for (File jar : pluginsDir.listFiles()) {
try {
ClassLoader loader = URLClassLoader.newInstance(
new URL[] { jar.toURL() },
getClass().getClassLoader()
);
Class<?> clazz = Class.forName(classpath, true, loader);
Class<? extends C> newClass = clazz.asSubclass(parentClass);
// Apparently its bad to use Class.newInstance, so we use
// newClass.getConstructor() instead
Constructor<? extends C> constructor = newClass.getConstructor();
return constructor.newInstance();
} catch (ClassNotFoundException e) {
// There might be multiple JARs in the directory,
// so keep looking
continue;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
throw new ClassNotFoundException("Class " + classpath
+ " wasn't found in directory " + System.getProperty("user.dir") + directory);
}
}
Использование кода:
ExtensionLoader<MyPlugin> loader = new ExtensionLoader<MyPlugin>();
somePlugin = loader.LoadClass("path/to/jar/file", "com.example.pluginXYZ", MyPlugin.class);
Некоторые примечания
Конечно, есть более эффективные способы сделать приложение расширяемым ( OSGi , Portlets , JPF и т. Д.), Но иногда вам просто нужно что-то такое простое.
Обратите внимание, что ExtensionLoader
вызывает только конструкторы
без аргументов (классы, подобные bean-компонентам). Может быть более
выгодно вернуть Constructor<? extends C>
и вызывает его с
соответствующими аргументами. В этом случае убедитесь, что вы загружаете
правильный конструктор из newClass.getConstructor()
.
ВНИМАНИЕ! В этом коде не выполняется никаких проверок безопасности, поэтому убедитесь, что вы доверяете загружаемым классам!