在运行时在应用程序中添加类

时间:2014-05-24 13:00:35

标签: java

我的软件中有一个抽象类,如下所示:

public abstract class Foo {   
    public abstract addAlgo(Object o);  
}

我想让不同的人有Foo的不同实现,以便在运行时动态保存和运行我的软件。我有一个主要课程如下:

public class Main {
    Foo f;
}

我希望我的用户在运行时提供Foo的实现。我正面临以下困难:

  1. 用户如何在运行时通过GUI添加其实现类?

  2. 如何在Main类的运行时实例化用户实现的对象?我希望用户的实现在将来成为我软件的永久部分,而不仅仅是在运行时...即我希望我的软件在运行时添加功能并永久存储该功能(即在{{1中更改)实际上我希望用户日常扩展我的软件......一旦用户在运行时添加他的功能类,他就可以在{{1}中查看他的对象实例化代码了。 } class(自动)如下:

    Main
  3. 我该怎么做?

2 个答案:

答案 0 :(得分:0)

要求用户以.jar文件的形式提供他的实现(以及任何可能属于那里的帮助器类,资源),并使用URLClassLoader按名称加载所需的类。 URL可以像

那样引用本地文件
   new URL("file:///home/myJars/myjar.jar") // Linux
   new URL("file:///c:/myJars/myjar.jar");  // Windows

在更复杂的情况下,您可能需要实现自己的类加载器,只要内部格式正确,就可以轻松地将任意字节数组转换为工作的java类。可以找到关于类加载器的好的入门课程here

只需使用一些纯文本文件即可记住运行之间的附加网址。

最简单的方法是调用URL类加载器上的loadClass(userClassName).newInstance()来获取用户提供的类的对象实例。用户类必须具有参数 - 较少的构造函数才能使其工作。

答案 1 :(得分:0)

我建议将java.net.URLClassLoaderjava.util.ServiceLoader结合使用以加载新类。

但是扩展Foo的类有一些限制,主要是必须有一个没有参数的构造函数(或默认构造函数)。此外,jars必须包含文件META-INF/services/<servicename>,其中<servicename>是服务的完整类名,即:mypackage.Foo;此文件必须包含一行,其中包含jar中实现服务的类的全名,并且应由ServiceLoader找到。在您的情况下,文件应该如下所示

META-INF /服务/ mypackage.Foo

myotherpackage.ConcreteFoo
myotherpackage.subpackage.OtherConcreteFoo

并且jar当然必须包含此文件中提到的所有类,它们必须是非抽象类,其构造函数不带参数。


然后,以下程序将创建Foo服务的所有实现的实例,URLClassLoader可以找到(即包括已经存在的所有实现)你的类路径):

public class Main {

    public static void main(String[] args) throws MalformedURLException {
        System.out.println(new File(".").getAbsolutePath());
        URLClassLoader urlloader = new URLClassLoader(
                      new URL[]{
                            // one example of a local jar file added
                            new File("../FooImplementations/dist/FooImplementations.jar").toURI().toURL()
             });

        for (Foo foo : ServiceLoader.load(Foo.class, urlloader)) {
            // do something with foo e.g.
            foo.addAlgo("Hello World!");
        }

    }

}

重新启动程序后再次查找网址:

  • 将网址保存到文件中,在应用程序启动时读取文件并使用上述方法获取Foo,或
  • 在启动程序时将jar复制/移动到类路径中的文件夹。 (然后可以使用默认的ClassLoaderServiceLoader.load(Class)
  • 加载类

如果无法使用no-parameter-constructor创建Foo实例,您仍然可以选择使用抽象工厂模式加载工厂Foo而不是Foo s直接(如果你必须创建每个Foo服务的多重实例,这也可能不是一个坏主意。)