我的软件中有一个抽象类,如下所示:
public abstract class Foo {
public abstract addAlgo(Object o);
}
我想让不同的人有Foo
的不同实现,以便在运行时动态保存和运行我的软件。我有一个主要课程如下:
public class Main {
Foo f;
}
我希望我的用户在运行时提供Foo
的实现。我正面临以下困难:
用户如何在运行时通过GUI添加其实现类?
如何在Main
类的运行时实例化用户实现的对象?我希望用户的实现在将来成为我软件的永久部分,而不仅仅是在运行时...即我希望我的软件在运行时添加功能并永久存储该功能(即在{{1中更改)实际上我希望用户日常扩展我的软件......一旦用户在运行时添加他的功能类,他就可以在{{1}中查看他的对象实例化代码了。 } class(自动)如下:
Main
我该怎么做?
答案 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.URLClassLoader
与java.util.ServiceLoader结合使用以加载新类。
但是扩展Foo的类有一些限制,主要是必须有一个没有参数的构造函数(或默认构造函数)。此外,jars必须包含文件META-INF/services/<servicename>
,其中<servicename>
是服务的完整类名,即:mypackage.Foo
;此文件必须包含一行,其中包含jar中实现服务的类的全名,并且应由ServiceLoader
找到。在您的情况下,文件应该如下所示
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!");
}
}
}
重新启动程序后再次查找网址:
ClassLoader
和ServiceLoader.load(Class)
)如果无法使用no-parameter-constructor创建Foo实例,您仍然可以选择使用抽象工厂模式加载工厂Foo
而不是Foo
s直接(如果你必须创建每个Foo
服务的多重实例,这也可能不是一个坏主意。)