Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guice: is it possible to inject modules?

Tags:

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...

like image 747
sxc731 Avatar asked Mar 31 '11 18:03

sxc731


People also ask

What does @inject do in Guice?

Note that the only Guice-specific code in the above is the @Inject annotation. This annotation marks an injection point. Guice will attempt to reconcile the dependencies implied by the annotated constructor, method, or field.

Is Guice lazy?

When does guice make the dependencies available? In general, construction is lazy (for non-singleton classes, this is intuitive - you don't know you need a new instance until someone tells you to inject one somewhere). The exception is for eager singletons which are constructed (wait for it) eagerly.

How do you inject a Guice class?

Client Application We need to create Injector object using Guice class createInjector() method where we pass our injector class implementation object. Then we use injector to initialize our consumer class. If we run above class, it will produce following output.

What is @inject annotation in Guice?

@Target(value={METHOD,CONSTRUCTOR,FIELD}) @Retention(value=RUNTIME) @Documented public @interface Inject. Annotates members of your implementation class (constructors, methods and fields) into which the Injector should inject values. The Injector fulfills injection requests for: Every instance it constructs.


2 Answers

@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.

like image 67
ColinD Avatar answered Oct 23 '22 12:10

ColinD


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.

like image 24
Jim Hurne Avatar answered Oct 23 '22 12:10

Jim Hurne