如何从文件夹加载所有已编译的类



我有一个文件夹操作符,在这个文件夹中我编译了文件(一个接口操作符和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()方法选择最好的类。有人能帮助我吗?

这有两个部分:

  1. 首先列出给定文件夹中的文件。
  2. 如果该类是给定的接口/类型,则加载该类。

我写了一个小方法来做这个——你可以基于它来写你的逻辑。

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机制来解决您的问题:

  1. 将存放操作类的目录添加到类路径

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将是您的基类或接口。

    相关内容

    • 没有找到相关文章