【问题标题】:Dependency Determination in Runtime运行时的依赖关系确定
【发布时间】:2012-03-11 14:02:15
【问题描述】:

当存在同一接口的多个实现并且此依赖项是在运行时根据参数定义时,我对如何使用 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 的存在,让 Guice 知道如果扩展与她匹配,它应该注入的依赖项是什么。

【问题讨论】:

    标签: java dependency-injection guice


    【解决方案1】:

    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() 加载类。

    • 1234563的URLs 指向每个文本文件。然后读取每个文件并使用Class.forName()加载类对象。
    • 最复杂的选项是使用annotation processing tool 作为编译过程的一部分,并使用它来生成文本文件或注册表类。这是一个单独的问题。

    【讨论】:

      猜你喜欢
      • 2013-10-22
      • 1970-01-01
      • 2014-11-20
      • 2020-10-07
      • 2019-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多