我正在用java开发一个基于组件的游戏引擎,现在,当我对组件进行更改时,我需要重新构建并重新启动编辑器,以使更改生效(或者,如果应用程序在调试模式下运行,我可以使用一些有限的热代码注入)。
我正在寻找一种方法,允许用户修改组件的源代码并重新加载它们,而不必重新启动应用程序(可能只需退出并进入游戏模式)。另外,我需要的一个重要特性是,最终导出的代码应该是本机Java代码(所以在最终结果中不应该使用解释器)。
你能给我一些关于如何在项目中集成beanshell解释器的建议吗?我可以手动监控源文件夹的变化,并将更新的java类提供给它,但是热交换到底是如何发生的呢?
发布于 2014-12-09 21:24:46
首先,标题有点让人困惑。您不需要集成BeanShell。你真正需要的是:
Java编译器要定义正确的architecture
架构
假设你有一个对象图。有很多对象,引用等,所以用新的实例替换一些实例真的是一项棘手的任务。你可以将动态部分隐藏在“静态”代理后面,而不是解决这个问题。代理将处理所有重新加载的内容(包括源文件夹监控)。
重新加载之前:
重新加载后:
完成此操作后,您可以轻松跟踪更改并在需要时更新动态部分。
Java编译器API
你可以不使用解释型语言,而是使用Java,动态编译并使用'Class.forName()‘加载。由于这种方法已经存在了一段时间,因此有很多不同的例子。
以下是一些详细信息:
发布于 2014-12-15 06:52:47
基本上,您希望实现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,please take a look上。祝你编码愉快!
发布于 2014-12-15 21:14:08
看看支持live-reloading的tapestry-ioc控制反转容器。
当处于开发模式(tapestry.product- mode =false)时,您可以实时重新加载您的服务。请注意,如果服务接口发生更改,您将需要重新启动。但是,对服务实现的任何更改,只要不改变服务接口,都可以实时重新加载。
https://stackoverflow.com/questions/27331252
复制相似问题