运行时的依赖性确定

时间:2012-03-11 14:02:16

标签: java dependency-injection guice

我怀疑如何在存在多个相同接口的实现时使用guice实现依赖注入,并且这种依赖是在运行时基于参数定义的,所以我将举一个例子来轻松解释我的问题:

想象一下,您有一个模块可以加载多种格式的文件的场景,基本上您有一个定义合同的接口,以及每种格式的多个实现:

public interface FileLoader {
    void load(File file);
}

public class YMLFileLoader{
    void load(File file){
    System.out.println("Loading YML");
    }
}

public class XMLFileLoader{
     void load(File file){
         System.out.println("Loading XML");
     }
}

现在,在运行时,guice必须根据文件扩展名定义必须用于加载它的实现。 我保持代码清洁的想法是使用注释,因为每个实现都指定了她通过@FileLoaderType注释加载的内容。

@Singleton
@FileLoaderType("yml")
public class YMLFileLoader{
    void load(File file)
    {
        System.out.println("Loading YML");
    }
}

@Singleton
@FileLoaderType("xml")
public class XMLFileLoader{
    void load(File file)
    {
        System.out.println("Loading XML");
    }
}

我的第一个问题是如果可以实施?

第一个问题是积极的,有没有办法实现这个解决方案,FileLoader的每个新实现都不需要在支持解决方案的AbstractModule的实现中进行重构? 换句话说,基本上每个FileLoader的新实现都只需要注释@FileLoaderType的存在才能知道如果扩展与她匹配,它应该注入什么依赖。

1 个答案:

答案 0 :(得分:3)

Guice无法做的一件事是它无法扫描你的类路径并找到你所拥有的类,所以你需要一些方法来告诉你有哪些类可用。因此,让我们将您的问题分成两半:获取FileLoader实现类的列表,并将这些类绑定到Guice。

让我先解决下半场问题。我假设你的AbstractModule子类中有一个名为getFileLoaderClasses的方法,带有签名:

private List<Class<? extends FileLoader>> getFileLoaderClasses() { ... }

在这种情况下,我建议绑定FileLoader实现的是这样的:

private void bindFileLoaders() {
  MapBinder<String, FileLoader> mapBinder
      = MapBinder.newMapBinder(binder(), String.class, FileLoader.class);
  for (Class<? extends FileLoader> implClass : getFileLoaderClasses()) {
    FileLoaderType annotation = implClass.getAnnotation(FileLoaderType.class);
    if (annotation == null) {
      addError("Missing FileLoaderType annotation on " + implClass.getClass());
      continue;
    }
    mapBinder.addBinding(annotation.getValue()).to(implClass);
  }
}

这需要guice-multibindings扩展名。绑定这样的实现后,可以将其用作:

public class MyLoader {
  @Inject Map<String, FileLoader> fileLoaderMap;

  public void load(File file, String type) {
    FileLoader fileLoader = fileLoaderMap.get(type);
    if (fileLoader == null) {
      throw new IllegalArgumentException("No file loader for files of type " + type);
    }
    fileLoader.load(file);
  }
}

现在,我们如何获得完整的实现类列表?有几种方法可以做到这一点:

  • 让一个类具有所有实现类的静态列表:

    public class FileLoaderRegistry {
      public static final List<Class<? extends FileLoader>> impls =
          ImmutableList.of(
            YMLFileLoader.class,
            XMLFileLoader.class,
            JsonFileLoader.class
          );
    }
    

    这样做的好处是它可能是最简单的解决方案。 它的缺点是您需要使用每个新实现更新这个文件,然后重新编译它。

  • 有一个包含所有类名的文本文件,并在阅读完文件的所有行后加载Class.forName()的类。

  • 如果您在不同的jar文件中有FileLoader个实现,那么您可以在一个公共位置使用一个文本文件列出每个jar中的类的名称,然后使用System.getResources()来获取Enumeration URLClass.forName()指向每个文本文件。然后读取每个文件并使用{{1}}加载类对象。

  • 最复杂的选项是在编译过程中使用annotation processing tool,并使用它来生成文本文件或注册表类。这是一个单独的问题。