当存在相同接口的多个实现,并且在运行时基于参数定义此依赖关系时,我不确定如何用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");
     }
}

现在,在运行时中,必须根据文件扩展名定义必须用于加载该文件的实现。
我保持代码整洁的想法是使用注释,因为每个实现都通过@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的存在即可使Guice知道扩展名与其匹配时应注入的依赖项是什么。

最佳答案

Guice无法做的一件事是,它无法扫描您的类路径并找到您拥有的类,因此您将需要某种方式来告诉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()获取指向每个文本文件的EnumerationURL。然后读取每个文件,并使用Class.forName()加载类对象。
  • 最复杂的选项是在编译过程中使用annotation processing tool,并使用它生成文本文件或注册表类。那是一个单独的问题。
  • 07-28 14:17