我有一个文件夹操作符,在这个文件夹中我编译了文件(一个接口操作符和4个实现操作符的类)。目的是从这个文件夹中加载所有的。class文件并在主程序中使用。我使用以下语句:
File operatorFile = new File("D:\operators");
URL operatorFilePath = operatorFile.toURL();
URL[] operatorFilePaths = new URL[]{operatorFilePath};
ClassLoader operatorsLoader = new URLClassLoader(operatorFilePaths);
//Plus,Minus,Multiply,Divide are classes that implement operator interface
Class[] operatorClass = new Class[]{ operatorsLoader.loadClass("Plus"), operatorsLoader.loadClass("Minus"),operatorsLoader.loadClass("Multiply") , operatorsLoader.loadClass("Divide") };
然后我用这个语句来调用.class文件方法:
Method methodsInOperator;
Object instance;
String operatorSign;
for(Class operatorCls : operatorClass)
{
instance = operatorCls.newInstance();
methodsInOperator = operatorCls.getMethod("getSign", null);
operatorSign = (String)methodsInOperator.invoke(instance, null);
if(operatorSign.equals(elementInExpression[2]))
{
methodsInOperator = operatorCls.getMethod("calculate", new Class[] { double.class, double.class } );
output =(double)methodsInOperator.invoke(instance, firstNumber, secondNumber);
}
}
但是下面的语句不能动态地工作,如果我们将另一个。class文件放到操作符文件夹中,程序将停止工作。
Class[] operatorClass = new Class[]{ operatorsLoader.loadClass("Plus"), operatorsLoader.loadClass("Minus"),operatorsLoader.loadClass("Multiply") , operatorsLoader.loadClass("Divide") };
我的目的是动态加载所有类,并检查它们是否实现操作符,并根据getSing()方法选择最好的类。有人能帮助我吗?
这有两个部分:
- 首先列出给定文件夹中的文件。 如果该类是给定的接口/类型,则加载该类。
我写了一个小方法来做这个——你可以基于它来写你的逻辑。
public Class[] getOperators(File operatorFile) throws MalformedURLException, ClassNotFoundException {
ClassLoader operatorsLoader = new URLClassLoader(new URL[] { operatorFile.toURI().toURL() });
File[] files = operatorFile.listFiles(new FilenameFilter() {
@Override public boolean accept(File dir, String name) {
return name.endsWith(".class");
}
});
ArrayList<Class> operators = new ArrayList<>();
for (File file : files) {
String className = file.getName().substring(0, file.getName().length() - 6);
Class<?> clazz = operatorsLoader.loadClass(className);
if(OperatorInterface.class.isAssignableFrom(clazz)) {
operators.add(clazz);
}
}
return operators.toArray(new Class[operators.size()]);
}
如果一切顺利,用文件夹调用这个方法应该返回该文件夹中实现OpeartorInterface的所有类。我没有执行它-所以你可能不得不修复一些问题与代码。
我建议使用spi机制来解决您的问题:
- 将存放操作类的目录添加到类路径
java -cp "main.jar;operations" my.package.MainClass
-
在操作目录
中添加一个名为"META-INF/services"的目录 创建一个包含所使用接口的完整限定名的文件。例如,如果接口的名称是Operation,并且在package com中。calc命名文件"com.calc.Operation"
用您选择的文本编辑器编辑此文件。通过完整限定名添加接口的每个实现。每个类在一个新的行中。你的第一行应该是"com.calc"。添加"第四行可以是"com.calc。强"等。
在你的主类中使用ServiceLocator-Class来迭代你的实现:
ServiceLoader<Operation> operations = ServiceLoader.load(Operation.class); Iterator<Operation> ite = operations.iterator(); while (ite.hasNext()) { Operation op = ite.next(); System.out.println("operation implementation is: " + op.getClass()); Number result = op.calc(3,4); System.out.println("Result of 3 and 4 is " + result); }
此SPI机制在https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html中描述这是一个非常古老的特性,但自从Java 6引入ServiceLocator以来,它变得非常方便。使用SPI,您不需要关心反射或类加载
如果你想添加更多的操作,添加类文件,并在operations/META-INF/services
如果我是你,我会给Reflections库一个机会。这是一个非常好的软件,可以让你做你想做的事情:
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setUrls(ClasspathHelper.forPackage("my.project.prefix"))
.setScanners(new SubTypesScanner()),
.filterInputsBy(
new FilterBuilder().includePackage("my.project.prefix")));
,其中my.project.prefix
指向您的文件夹。
reflections
实例来获取您的类:
Set<Class<? extends Operator>> operators =
reflections.getSubTypesOf(Operator.class);
这里,Operator
将是您的基类或接口。