Guice: One “Provider” for multiple implementations

后端 未结 2 1363
夕颜
夕颜 2021-01-16 15:28

I have an interface that has 20 or so annotated implementations. I can inject the correct one if I know which I need at compile time, but I now need to dynamically inject on

相关标签:
2条回答
  • 2021-01-16 15:56

    Inject a MapBinder.

    In your module, load the bindings into the MapBinder, then make your runtime parameters injectable as well. This example is based on the one in the documentation:

    public class SnacksModule extends AbstractModule {
      protected void configure() {
        MapBinder<String, Snack> mapbinder
               = MapBinder.newMapBinder(binder(), String.class, Snack.class);
        mapbinder.addBinding("twix").to(Twix.class);
        mapbinder.addBinding("snickers").to(Snickers.class);
        mapbinder.addBinding("skittles").to(Skittles.class);
      }
    }
    

    Then, in your object, inject the Map and the parameter. For this example I will assume you've bound a java.util.Properties for your runtime parameters:

    @Inject
    public MyObject(Map<String, Provider<Snack>> snackProviderMap, Properties properties) {
      String snackType = (String) properties.get("snackType");
      Provider<Snack> = snackProviderMap.get(property);
      
      // etc.
    }
    

    Note, with the same MapBinder you can inject either a simple Map<String, Snack> or a Map<String, Provider<Snack>>; Guice binds both.

    0 讨论(0)
  • 2021-01-16 16:09

    If all you want is to get an instance programmatically, you can inject an Injector. It's rarely a good idea--injecting a Provider<T> is a much better idea where you can, especially for the sake of testing--but to get a binding reflectively it's the only way to go.

    class YourClass {
      final YourDep yourDep;  // this is the dep to get at runtime
    
      @Inject YourClass(Injector injector) {
        YourAnnotation annotation = deriveYourAnnotation();
        // getProvider would work here too.
        yourDep = injector.getInstance(Key.get(YourDep.class, annotation));
      }
    }
    

    If you're trying write a Provider that takes a parameter, the best way to express this is to write a small Factory.

    class YourDepFactory {
      @Inject @A Provider<YourDep> aProvider;
      @Inject @B Provider<YourDep> bProvider;
      // and so forth
    
      Provider<YourDep> getProvider(YourParameter parameter) {
        if (parameter.correspondsToA()) {
          return aProvider;
        } else if (parameter.correspondsToB()) {
          return bProvider;
        }
      }
    
      YourDep get(YourParameter parameter) {
        return getProvider(parameter);
      }
    }
    
    0 讨论(0)
提交回复
热议问题