Guice: is it possible to inject modules?

18,272

Solution 1

@Provides methods can take dependencies as parameters just like parameters to an @Inject annotated constructor or method:

@Provides Something provideSomething(Dependency d) {
   return new Something(d); // or whatever
}

This is documented here, though perhaps it could be made to stand out more.

Solution 2

Using a provider or @Provides methods are great if you need a dependency to manually construct an object. However, what if you need something to help you decide how to configure the bindings themselves? It turns out you can use Guice to create (and configure) your module.

Here is a (contrived) example. First, the module we want to configure:

/**
 * Creates a binding for a Set<String> which represents the food in a pantry.
 */
public class PantryModule extends AbstractModule {
  private final boolean addCheese;

  @Inject
  public ConditionalModule(@Named("addCheese") boolean addCheese) {
    this.addCheese = addCheese;
  }

  @Override
  protected void configure() {
    Multibinder<String> pantryBinder = Multibinder
      .newSetBinder(binder(), String.class);

    pantryBinder.addBinding().toInstance("milk");

    if (addCheese) {
      pantryBinder.addBinding().toInstance("cheese");
    }

    pantryBinder.addBinding().toInstance("bread");
  }
}

The PantryModule expects a boolean value to be injected to decide whether or not it should include cheese in the pantry.

Next, we'll use Guice to configure the module:

// Here we use an anonymous class as the "configuring" module. In real life, you would 
// probably use a standalone module.
Injector injector = Guice.createInjector(new AbstractModule() {
  @Override
  protected void configure() {
    // No cheese please!
    bindConstant().annotatedWith(Names.named("addCheese")).to(false);
    bind(PantryModule.class);
  }
});

Module configuredConditionalModule = injector.getInstance(PantryModule.class);

Now that we have a configured module, we'll update our injector to use it...

//...continued from last snippet...
injector = injector.createChildInjector(configuredConditionalModule);

And finally we'll get the set of strings that represent our pantry:

//...continued from last snippet...
Set<String> pantry = injector.getInstance(new Key<Set<String>>() {});

for (String food : pantry) {
  System.out.println(food);
}

If you put all the pieces together in a main method and run it, you'll get the following output:

milk
bread

If you change the binding to the "addCheese" boolean to true, you'll get:

milk
cheese
bread

This technique is cool, but probably only useful when you have control over the Injector instance and only when the module requires complex dependencies. Nonethless, I found a real need for this on a real project at work. If I did, then someone else might too.

Solution 3

The question is already well answered, but I just wanted to add a variation to Colin's example:

class MyModule extends AbstractModule { 
  public void configure() {
    bind(Something.class).toProvider(new Provider<Something>() {
       @Inject Dependency d;
       Something get() { return d.buildSomething(); }
    }
  }
}

The @Provides method approach is clearer than what I have above for this simple case, but I've found that instantiating an actual Provider can be useful in some situations too. Something I stole from the mailing list; wouldn't have occurred to me on my own ;)

Share:
18,272

Related videos on Youtube

sxc731
Author by

sxc731

Updated on February 01, 2020

Comments

  • sxc731
    sxc731 about 4 years

    I have a Module that requires some Depedency. Is there a way Modules themselves can be injected? I realize this is a bit of a chicken and egg situation...

    Example:

    public class MyModule implements Module {
    
        private final Dependency d_;
    
        @Inject public MyModule(Dependency d) {
            d_ = d;
        }
    
        public void configure(Binder b) { }
    
        @Provides Something provideSomething() {
            // this requires d_
        }
    }
    

    I suppose in this case the solution would be to turn the @Provides method into a full-fledged Provider<Something> class. This is clearly a simplified example; the code I'm dealing with has many such @Provides methods so cutting them each into individual Provider<...> classes and introducing a module to configure them adds a fair amount of clutter - and I thought Guice was all about reducing boilerplate clutter?

    Perhaps it's a reflection of my relative noobyness to Guice but I've come across a fair few cases where I've been tempted to do the above. I must be missing something...

  • sxc731
    sxc731 about 13 years
    Thanks Colin and you're right, I overlooked the official doc, which is actually very clear if a little too concise (normally a very good thing). Lesson learned (study the official doc more closely) but hopefully a useful thing to call out for others too!
  • sxc731
    sxc731 about 13 years
    I know I'm asking for a lot here but now I've found about this feature (and boy, is it handy!), it would be delightful if it could be combined with the @Nullable annotation. eg: in the above example, this would make Dependency an optional binding. Alas, this doesn't seem to work (Guice 2.0 complains that "No Implementation for Dependency was bound"). Wondering if this could be a feature of a forthcoming version of Guice or maybe there's a good reason not to have this?
  • ColinD
    ColinD about 13 years
    @Christian: @Nullable doesn't make dependencies optional, it allows null to be bound using toProvider(Providers.<Dependency>of(null)). Don't know that there's any way to make a specific dependency to an @Provides method optional.
  • sxc731
    sxc731 about 13 years
    Many thanks Colin, I see have a few things to learn about Guice ;-)
  • Antony Stubbs
    Antony Stubbs over 11 years
    This solved my inter-dependency issue as well. I think it should definitely be made more clear that you can use it like this, on the wiki.
  • B Medeiros
    B Medeiros about 10 years
    I'm doing something similar, and I thought I could be going crazy.. I feel better now, I know that I'm not alone ;)
  • Arlunn
    Arlunn over 3 years
    Is there a reason why you didn't just use field injection for the boolean?

Related