简体   繁体   English

运行时的依赖性确定

[英]Dependency Determination in Runtime

I'm with some doubts how to implement dependency injection with guice when exists multiple implementations of same interface and this dependency is defined at runtime based on parameters, so I'll give an example to easily explain my question: 我怀疑如何在存在多个相同接口的实现时使用guice实现依赖注入,并且这种依赖是在运行时根据参数定义的,所以我将举一个例子来轻松解释我的问题:

Imagine the scenario where you have one module to load files of multiple formats, basically you have one interface defining the contract, and multiple implementations one for each format: 想象一下,您有一个模块可以加载多种格式的文件,基本上您有一个定义合同的接口,以及每种格式的多个实现:

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

Now, in runtime guice have to define based on file extension the implementation that must be used to load it. 现在,在运行时,guice必须根据文件扩展名定义必须用于加载它的实现。 My idea to maintain the code clean is make use of annotations, for each implementation is specified what she loads through @FileLoaderType annotation. 我保持代码清洁的想法是使用注释,因为每个实现都指定了她通过@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");
    }
}

My first question is if implementation is possible? 我的第一个问题是如果可以实施?

Being the first question is positive, there is any way to implement this solution where for each new implementation of FileLoader doesn't require refactoring in implementation of AbstractModule that supports the solution? 作为第一个问题是积极的,有任何方法可以实现这个解决方案,其中FileLoader的每个新实现都不需要在支持解决方案的AbstractModule的实现中进行重构? In other words, is basically for each new implementation of FileLoader only be required the existence of the annotation @FileLoaderType to Guice know what is the dependency it should inject if the extention match with her. 换句话说,基本上每个FileLoader的新实现都只需要注释@FileLoaderType的存在才能知道如果扩展与她匹配,它应该注入什么依赖。

One thing Guice can't do is it can't scan your classpath and find what classes you have, so you will need some way to tell guice what classes you have available. Guice无法做的一件事是它无法扫描你的类路径并找到你所拥有的类,所以你需要一些方法来告诉你有哪些类可用。 Therefore let's split your question into two halves: getting a list of FileLoader implementation classes, and binding those classes into Guice. 因此,让我们将您的问题分成两半:获取FileLoader实现类的列表,并将这些类绑定到Guice中。

Let me tackle the second half first. 让我先解决下半场问题。 I'll assume that you have in your AbstractModule subclass a method called getFileLoaderClasses with the signature: 我假设你的AbstractModule子类中有一个带有签名的getFileLoaderClasses方法:

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

In that case, what I would recommend for binding the FileLoader implementations is something like this: 在这种情况下,我建议绑定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);
  }
}

This requires the guice-multibindings extension. 这需要guice-multibindings扩展。 Once you bind the implementations like that, you can use it as: 绑定这样的实现后,您可以将其用作:

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

So now, how do we get that full list of implementation classes? 那么现在,我们如何获得完整的实现类列表? There are a few ways to do this: 有几种方法可以做到这一点:

  • Have one class with a static list of all the implementation classes: 让一个类具有所有实现类的静态列表:

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

    This has the advantage that it's probably the simplest solution to implement. 这样做的好处是它可能是最简单的解决方案。 It has a disadvantage that you'll need to update this one file with each new implementation, and recompile it. 它的缺点是您需要使用每个新实现更新这个文件,然后重新编译它。

  • Have a text file with all the class names in it and load the classes with Class.forName() after reading all the lines of the file. 有一个包含所有类名的文本文件,并在读完文件的所有行后加载Class.forName()的类。

  • If you have the FileLoader implementations in different jar files, you can have a text file listing the names of the classes in each jar in a common location and then use System.getResources() to get an Enumeration of URL s pointing to each of the text files. 如果你在不同的jar文件中有FileLoader实现,你可以有一个文本文件列出公共位置中每个jar中的类的名称,然后使用System.getResources()来获取指向每个jar的URLEnumeration文本文件。 Then read each file and load the class objects with Class.forName() . 然后读取每个文件并使用Class.forName()加载类对象。

  • The most complicated option is to use the annotation processing tool as part of your compilation process, and use that to generate either text files or the registry class. 最复杂的选项是使用注释处理工具作为编译过程的一部分,并使用它来生成文本文件或注册表类。 That's a separate question. 这是一个单独的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM