动态加载遵循接口的类

时间:2012-10-04 14:00:27

标签: java reflection interface

我有几个实现两个接口的类。所有这些都实现了 BaseInterface 和其他一些特定于它们的接口。

我希望能够使用下面的 loadClass 方法来实例化 .properties 文件中引用的类,并调用它们都包含的公共方法(因为他们实现 BaseInterface )。

public interface BaseInterface {
    public void doBase();
}

public interface SpecificInterface extends BaseInterface {
    public void doSpecific();
}

public class SpecificClass implements SpecificInterface {
    public void doBase() { ... }

    public void doSpecific() { ... }
}

public class LoadClass() {
    private PropertiesLoader propertiesLoader = new PropertiesLoader();

    public <C extends BaseInterface> C loadClass(String propertyName) {
        Class<C> theClass;

        // Load the class.
        theClass = propertiesLoader.getPropertyAsClass(propertyName);

        // Create an instance of the class.
        C theInstance = theClass.newInstance();

        // Call the common method.
        theInstance.doBase();

        return theInstance;
    }
}

不幸的是,当我运行代码时:

loadClassInstance.loadClass("SpecificClass");

我得到以下异常:

Exception in thread "main" java.lang.ClassCastException:
SpecificClass cannot be cast to BaseInterface
at LoadClass.loadClass

我是如何解决这个问题的?

非常感谢,Danny

3 个答案:

答案 0 :(得分:13)

Java的服务提供程序接口(SPI)库允许您根据它们实现的接口动态加载具有公共无参数构造函数的类,并且所有这些都是通过使用META-INF/services来完成的。

首先,您需要interface

package com.example;

public interface SomeService {

    String getServiceId();

    String getDisplayName();
}

然后当您需要它们时,可以使用Java ServiceLoader类加载它们,该类实现Iterable

ServiceLoader<SomeService> loader = ServiceLoader.load(SomeService.class);
for (SomeService serv : loader) {
    System.out.println(serv.getDisplayName());
}

然后,当您在类路径上有一个或多个实现类时,它们会在META-INF/services中注册。所以如果你有实现:

package com.acme;

public class SomeImplementation implements SomeService {

    // ...

    public SomeImplementation() { ... }

    // ...
}

请注意,此类需要默认的no-args构造函数,这不是可选的。

通过在类路径中的META-INF/services中创建一个文件(例如在jar的根目录中),使用以下属性在类加载器中注册它:

  1. 文件的名称是界面的完全限定类名,在这种情况下,它是com.example.SomeService
  2. 该文件包含以换行符分隔的实现列表,因此对于示例实现,它将包含一行:com.acme.SomeImplementation
  3. 你走了,就是这样。如何构建项目将决定放置META-INF/services内容的位置。 Maven,Ant等都有办法解决这个问题。如果您在构建中添加这些文件时遇到任何问题,我建议您询问有关特定构建过程的其他问题。

答案 1 :(得分:1)

如果您用下面的代码替换它,它可以工作。我怀疑PropertiesLoader正在做一些不应该做的事情。

    Class<?> theClass;
    // Load the class.
    theClass = Class.forName("SpecificClass");
    // Create an instance of the class.
    C theInstance = (C) theClass.newInstance();


   BaseInterface base =  loadClass();//There is no problem in casting

答案 2 :(得分:0)

Java程序通常由系统类加载器加载。 .properties文件中引用的类由用户定义的类加载器加载。由不同的类加载器加载的类被认为是不同的,即使它们具有相同的名称并且从相同的类文件加载。在您的情况下,系统类加载器加载的接口BaseInterface与加载的BaseInterface不同 PropertiesLoader。 要解决此问题,PropertiesLoader应该将BaseInterface的加载委托给系统类加载器。这样做的典型方法是使用系统类加载器作为PropertiesLoader的父类加载器。