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

Java 如何从文件夹中加载所有已编译的类?,java,reflection,Java,Reflection,我有一个文件夹操作符。在这个文件夹中我编译了文件(一个接口操作符和4个实现操作符的类)。目的是从这个文件夹加载所有的.class文件并在主程序中使用。我使用以下语句: File operatorFile = new File("D:\\operators"); URL operatorFilePath = operatorFile.toURL(); URL[] operatorFilePaths = new URL[]{operatorFilePath

我有一个文件夹操作符。在这个文件夹中我编译了文件(一个接口操作符和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文件放入operators文件夹,程序将停止工作

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()]);
    }
    
    public类[]getOperators(File Operator File)引发畸形的异常,ClassNotFoundException{
    ClassLoader operatorsLoader=新的URLClassLoader(新的URL[]{operatorFile.toURI().toURL()});
    File[]files=operatorFile.listFiles(新文件名过滤器(){
    @重写公共布尔接受(文件目录,字符串名称){
    返回名称.endsWith(“.class”);
    }
    });
    ArrayList运算符=新的ArrayList();
    用于(文件:文件){
    字符串className=file.getName().substring(0,file.getName().length()-6);
    Class clazz=operatorsLoader.loadClass(className);
    if(运算符接口.class.isAssignableFrom(clazz)){
    运算符。添加(clazz);
    }
    }
    返回操作符.toArray(新类[operators.size()]);
    }
    

    如果一切顺利,则使用文件夹调用此方法应返回该文件夹中实现OpeartorInterface的所有类。我没有执行它-因此您可能必须修复代码中的一些问题。

    我建议使用SPI机制解决您的问题:

  • 将存储操作类的目录添加到类路径
  • java-cp“main.jar;operations”my.package.MainClass

  • 在操作目录中添加一个名为“META-INF/services”的目录

  • 创建一个具有所用接口的完整限定名的文件。例如,如果接口的名称为Operation,并且在包com.calc中命名文件“com.calc.Operation”

  • 使用您选择的文本编辑器编辑此文件。通过完整的限定名添加接口的每个实现。每节课换一行。第一行是“com.calc.Addition”,第四行是“com.calc.division”等等

  • 在主类中,只需使用ServiceLocator类来迭代实现:

    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);
    }
    
    ServiceLoader operations=ServiceLoader.load(Operation.class);
    迭代器ite=operations.Iterator();
    while(ite.hasNext()){
    操作op=ite.next();
    System.out.println(“操作实现为:+op.getClass());
    数字结果=运算计算(3,4);
    System.out.println(“3和4的结果是”+结果);
    }
    
  • 中描述了此SPI机制 这是一个非常古老的特性,但自从Java6引入ServiceLocator之后,它就变得非常方便了。使用SPI,您不需要关心反射或类加载


    如果您想添加更多操作,请添加类文件,并在operations/META-INF/services下的文件中添加一行具有完整限定名的新行。这是一款非常好的软件,可以让您完全按照自己的意愿进行操作:

    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);
    

    set“程序停止工作”是什么意思。新类没有进入
    opeartorClass
    数组?例如,我写了%class并将.class添加到文件夹中,我的程序应该仍然可以再次澄清,新的百分比应该出现在您的
    operatorClass
    数组中?是的,我应该动态加载类。添加到文件夹中的每个.class文件也应该加载。您几乎肯定不需要加载所有类。就是你直接使用的那个。它所依赖的其他文件将自动加载。