Guice and interface that has multiple implementations
Asked Answered
R

4

20

If I have interface Validator and multiple implementations for this interface. How can I inject any of the multiple implementations with Guice? Now I know that I can use following code to inject one, but it allows only one implementation:

public class MyModule extends AbstractModule {
  @Override 
  protected void configure() {
    bind(Validator.class).to(OneOfMyValidators.class);
  }
}

What I would like to do is:

Validator v1 = injector.getInstance(Validator1.class);
Validator v2 = injector.getInstance(Validator2.class);

Is it possible at all?

Rakish answered 8/11, 2011 at 6:41 Comment(2)
Your example does not inject the validator, it merely creates them. How do you want to distinguish your 1456 implementations in real code?Pinfold
True. I meant that application can have x number of validators. And maybe validator creator wants to inject some dependecies to validators. So then validator creator could add @inject to validator constructor and get required dependencies.Rakish
P
25

Short answer: binding annotations. They're basically a way of letting the depender give a hint that points towards a particular instance or implementation without requiring a dependency on the full concrete implementation class.

See: https://github.com/google/guice/wiki/BindingAnnotations

For example, in the module, you might do:

bind(Validator.class).annotatedWith(ValidatorOne.class).to(OneOfMyValidators.class);
bind(Validator.class).annotatedWith(ValidatorTwo.class).to(SomeOtherValidator.class);

And in your constructor, you'd do:

@Inject
MyClass(@ValidatorOne Validator someValidator,
    @ValidatorTwo Validator otherValidator) {
  ...
}

To get an annotated value straight from an Injector, you'll have to use the Guice Key class, like:

Validator v1 = injector.getInstance(Key.get(Validator.class, ValidatorOne.class));

On a side note, binding annotations are very useful for injecting runtime constants. See the comments for bindConstant in:

https://google.github.io/guice/api-docs/latest/javadoc/index.html?com/google/inject/Binder.html

Punctilio answered 8/11, 2011 at 9:18 Comment(3)
Thank you for answer, but I don't know if this is suitable solution for my situation, what if there are 1456 validators? Then I would need 1456 annotations, one for every validator, and I already have generic annotation for validators.Rakish
Let me take a step back and ask: "How do your want your validation-dependent classes to ask for these validators?" Or, in an ideal world, what would their @Inject constructors look like? Do you want code to ask for specific instances? (e.g. @ValidatorOne Validator someValidator). Or, would classes maybe want to depend on a Set<Validator>? (if so, Multibindings are handy). Or should code maybe depend on a single Validator that's internally composed of many Validators? The answer to that will definitely influence the optimal solution.Punctilio
You can use named Keys instead of annotations @Named("validator1")Therapsid
S
12

I found this thread when looking for a solution for dynamically binding multiple implementations to an interface, similar to ServiceLoader in Java. The answer covers a more general case, but it can also be used to obtain a particular implementation from the set. Multibinder allows to bind multiple implementations to a type:

public class ValidatorsModule extends AbstractModule {
  protected void configure() {
      Multibinder<Validator> multibinder
          = Multibinder.newSetBinder(binder(), Validator.class);
      multibinder.addBinding().toInstance(new ValidatorOne());
      multibinder.addBinding().toInstance(new ValidatorTwo());
  }
}

//Usage
@Inject Set<Validator> validators;
Stocking answered 25/3, 2014 at 21:39 Comment(4)
I dont have Multibinder in com.google.inject namespace. I assume its in a different version.....which version of juice supports this?Orellana
Multibinder was introduced in Guice 2.0. Which version are you using?Stocking
4.1.0 per my pom file: <dependency> <groupId>com.google.inject</groupId> <artifactId>guice</artifactId> <version>4.1.0</version> </dependency>Orellana
You need guice-multibindings module in addition to guice. See #8689038Stocking
E
9

Very similar to ejboy's proposal, but since you own different Validator classes, you can bind to the classes itself, not creating instances manually.

protected void configure() {
   ...
   Multibinder<Validator> mb = Multibinder.newSetBinder(binder(), Validator.class);
   mb.addBinding().to(Validator1.class);
   mb.addBinding().to(Validator2.class);
   mb.addBinding().to(Validator3.class);
   ...
}

Then viewed from the perspective of usage, e.g. by Constructor Injection:

class UseCase {
    private Set<Validator> allOfThem;

    @Inject
    public UseCase(Set<Validator> allOfThem) {
        this.allOfThem = allOfThem;
        // e.g. iteratation
        for (Validator oneOfThem : allOfThem) {
            ...
        }
    }
}
Enchase answered 24/11, 2014 at 9:54 Comment(0)
C
0

Kotlin

This is how we can do binding for multiple implementations for an interface

Class SomeModule : AbstractModule() {
        
            override fun configure() {
                val myBinder: Multibinder<MyInterface> = Multibinder.newSetBinder(binder(), MyInterface::class.java)
                myBinder.addBinding().to(Implementation1::class.java)
                 myBinder.addBinding().to(Implementation2::class.java)
}

Usage

@Inject constructor(private val someVar:Set<@JvmSuppressWildcards MyInterface>)
Consubstantiate answered 8/6, 2021 at 16:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.