Как интегрировать beanshell
Я разрабатываю игровой движок на основе компонентов в java, прямо сейчас, когда у меня есть изменения в компонентах, которые мне нужно перестроить и перезапустить редактор, чтобы изменения вступили в силу (или я могу использовать ограниченную инъекцию горячего кода, если приложение работает в режиме отладки).
Я ищу способ разрешить пользователю изменять источник компонентов и перезагружать их без необходимости перезапуска приложения (возможно, просто выйти и войти в игровой режим). Также важной особенностью, которая мне нужна, является то, что конечный экспортированный код должен быть родным Java-кодом (поэтому в конечном результате интерпретатор не должен использоваться)).
Можете ли вы дать мне какие-либо указания о том, как интегрировать интерпретатор beanshell в проект? Я могу вручную отслеживать исходную папку для изменений и кормить обновленные классы java, но как происходит горячая замена?
Ответы
Ответ 1
Прежде всего, заголовок немного запутан. Вам не нужно интегрировать BeanShell. Вам действительно нужны:
- для определения правильной архитектуры
- использовать Java Compiler API для работы с классами Java
Архитектура
Скажем, у вас есть граф объектов. Есть много объектов, ссылок и т.д. Таким образом, будет действительно сложной задачей заменить какой-то экземпляр на новый. Вместо решения этой проблемы вы можете скрыть динамическую часть за "статическим" прокси. Прокси будет обрабатывать все материалы перезагрузки (включая мониторинг исходных папок).
Перед перезагрузкой:
![enter image description here]()
После перезагрузки:
![enter image description here]()
После этого вы можете легко отслеживать изменения и обновлять динамическую часть при необходимости.
API компилятора Java
Вместо использования интерпретируемых языков вы можете использовать Java, скомпилировать его "на лету" и загрузить с помощью "Class.forName()". Существует много разных примеров из-за того, что этот подход был на некоторое время.
Вот несколько деталей:
Ответ 2
В основном вы хотите реализовать extensibility или шаблон дизайна плагина. Существует несколько способов реализации этого сценария.
Какой бы компонент, который вы хотите разрешить кому-либо другому, перезагрузить модуль, определить интерфейс и реализовать свою собственную реализацию по умолчанию. Например, здесь я пытаюсь предоставить HelloInterface, который каждая страна может реализовать и загрузить в любое время,
public interface HelloInterface {
public String sayHello(String input);
..
}
public class HelloImplDefault implements HelloInterface {
public String sayHello(String input) {
return "Hello World";
}
}
Теперь разрешите пользователю добавлять файлы плагинов (пользовательской реализации) к предварительно сконфигурированному пути. Вы можете либо использовать файл FileSystemWatcher, либо фоновый поток для сканирования этого пути и попытаться скомпилировать и загрузить файл.
Чтобы скомпилировать java файл,
private void compile(List<File> files) throws IOException{
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager
.getJavaFileObjectsFromFiles(files);
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null,
null, compilationUnits);
boolean success = task.call();
fileManager.close();
}
Чтобы загрузить файл класса,
private void load(List<File> files) throws MalformedURLException, InstantiationException, IllegalAccessException, ClassNotFoundException{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
for(File f: files){
if(f.getName().endsWith(".class") && !loadedClass.contains(f.getName())){
URL url = f.toURL();
URL[] urls = new URL[]{url};
Object obj = cl.loadClass(f.getName().replace(".class", "")).newInstance();
if(obj instanceof HelloInterface){
HelloProviders.addProvider((HelloInterface)obj);
System.out.println("Loaded "+ ((HelloInterface)obj).getProviderName());
}else{
//Add more classes if you want
}
loadedClass.add(f.getName());
}
}
}
На этом этапе вы можете прочитать пользовательскую реализацию и загрузить ее в системный загрузчик. Теперь вы готовы к работе. Для этого подхода есть последствия для безопасности, которые вам нужно изучать в Интернете.
Я реализовал один пример кода и опубликовал в github, пожалуйста, посмотрите. Счастливое кодирование!
Ответ 3
Взгляните на tapestry-ioc инверсию контейнера управления, которая поддерживает жить-перегрузочные.
Когда в режиме разработки (tapestry.production-mode = false) вы можете перезагрузить свои службы. Обратите внимание, что при изменении интерфейса службы вам потребуется перезагрузка. Но любые изменения в реализации службы, которые не изменяют интерфейс службы, могут быть перезагружены.