我需要一些关于范式的建议。之前,我有类似的东西
package/Instantiator.class
package/instances/GenericInstance.class (abstract)
package/instances/AInstance.class (extends Generic)
package/instances/BInstance.class (extends Generic)
Instantiator
所做的是搜索package/instances
文件夹中的所有类文件,并使用反射对每个文件进行实例化,并在所有实例上调用抽象方法并收集输出并保存到数据库中。
但是,我现在必须将我的代码打包到一个Jar中,而Java似乎不允许在jar中的包中搜索所有类文件(因为它似乎混淆了路径)。
我可以在GenericInstance中的List中添加所有实例,然后Instantiator可以获取类列表。
但我希望其他人能够在该软件包中添加一个类,就是这样。
我应该遵循什么模式?任何代码帮助?谢谢!
答案 0 :(得分:1)
JDK中有一个内置解决方案,即ServiceLoader
(Java 6+)。但是,它要求用户在META-INF/services
中创建一个包含实现列表的文件。
如果您的基本界面是package.instances.GenericInstance
,那么您的文件将被称为META-INF/services/package.instances.GenericInstance
,其内容将是:
path.to.implementation1
path.to.implementation2
等。根据您使用的构建系统,可能会自动为您生成此类文件(maven有一个插件,请参阅here)。
Java似乎不允许在jar中的包中搜索所有类文件
是的,它可以(但是执行它的代码非常复杂 - 你必须创建一个URLClassLoader
等)。
一个解决方案,如果所有jar在运行时都在你的类路径中,那就是使用像reflections这样的东西(奖励:取决于番石榴,所以你得到它的所有细节),它有类别等的有效过滤器,以及可以“反映”除“系统”类加载器之外的类加载器;所以这也可以。
答案 1 :(得分:0)
我刚刚发现的方法:
try {
String jarName = new File(Insantiator.class.getProtectionDomain()
.getCodeSource()
.getLocation()
.getPath())
.getName();
JarFile jar = new JarFile(jarName);
Enumeration<JarEntry> entries = jar.entries();
while(entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
if(name.contains("package/instances") && !name.contains("GenericInstance.class") && name.contains(".class")) {
name = name.replace("/", ".").replace(".class", "");
try {
Class theClass = Class.forName(name);
GenericInstance instance = (GenericInstance ) theClass.newInstance();
instances.add(instance);
} catch(InstantiationException | IllegalAccessException | ClassNotFoundException e) {
Utilities.writeLog("---- tried: " + name);
Utilities.writeLogException(null, e);
}
}
}
} catch (IOException e) {
Utilities.writeLogException(null, e);
}