如何集成BeanShell

10
我正在开发一个基于组件的Java游戏引擎,目前当我修改组件时,需要重新构建并重启编辑器才能使更改生效(或者在调试模式下运行应用程序时可以使用一些有限的热代码注入)。
我正在寻找一种方式,允许用户修改组件的源代码并重新加载它们,而无需重新启动应用程序(可能只需退出并进入游戏模式)。另一个重要的功能是导出的最终代码应该是本地Java代码(因此最终结果中不应使用解释器)。
你能给我一些关于如何将Beanshell解释器集成到项目中的指引吗?我可以手动监视源文件夹的更改并将更新后的Java类提供给它,但真正的热交换将会如何进行呢?

http://www.beanshell.org/manual/embeddedmode.html - Pratik Butani
我想知道为什么没有人提到OSGi。http://en.wikipedia.org/wiki/OSGi - Yser
3个回答

4
首先,标题有点令人困惑。您不需要集成BeanShell。您实际需要的是:
  1. 定义适当的架构
  2. 使用Java编译器API来处理java类
架构 假设您有一个对象图。有许多对象,引用等。因此,将某个实例替换为新实例将是非常棘手的任务。您可以通过在“静态”代理后面隐藏动态部分来解决此问题。代理将处理所有重新加载内容(包括源文件夹监视)。
重新加载之前:

enter image description here

重新加载后:

enter image description here

完成后,您可以轻松跟踪更改并在需要时更新动态部分。

Java编译器API

与使用解释性语言不同,您可以使用Java,在运行时进行编译,并使用'Class.forName()'进行加载。由于这种方法已经存在一段时间,因此有很多不同的示例。

以下是一些详细信息:


我的问题非常具体,就是如何集成beanshell来进行热交换,但是没有人回答。我不想手动重新编译代码和手动管理动态重载,值得注意的是,在动态和静态部分之间设置代理是不可行的,因为它会在最终设备上产生额外的开销(其中包括一些旧的j2me设备)。 - Anton Banchev
你的回答看起来对于其他遇到类似问题的人来说最为完整,这就是我选择它作为悬赏的原因。 - Anton Banchev

0

基本上,您想要实现可扩展性或插件设计模式。有多种方法可以实现此场景。

无论您想要允许其他人重新加载模块的组件是哪个,都要定义一个接口并将自己的实现作为默认实现。例如,这里我正在尝试提供一个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上,请查看。祝编码愉快!


0

看一下支持实时重载Tapestry IOC控制反转容器。

当处于开发模式(tapestry.production-mode=false)时,您可以实时重新加载服务。请注意,如果服务接口更改,您需要重新启动。但是,任何不改变服务接口的服务实现更改都可以进行实时重新加载。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接