Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guice / DI and mixing injection and parameters passed in at runtime

I have a situation where when I initialize some of my classes, some of the fields I need to be injected (e.g. references to factories etc) whereas some others are dynamic and created at runtime (e.g. usernames etc). How do I construct such objects using the GUICE framework? Simply annotating the fields I need injected as @Inject doesn't work as they seem to not be set up when creating an object using the constructor. For instance:

class C {
   @Inject
   private FactoryClass toBeInjected;

   private ConfigurationField passedIn;

   public C(ConfigurationField passedIn) {
      this.passedIn = passedIn;
   }
}

If my understanding is correct (and I could be wrong), the fact that I'm creating a new instance of C via new and not through Guice means that no injection will take place. I do need to pass these parameters in the constructor, but also want some fields injected -- so how do I solve this problem?

like image 389
Liv Avatar asked May 13 '15 18:05

Liv


1 Answers

A feature specifically matching "mixing injection and parameters passed" would be Assisted Injection.

class C {
  // Guice will automatically create an implementation of this interface.
  // This can be defined anywhere, but I like putting it in the class itself.
  interface Factory {
    C create(ConfigurationField passedIn);
  }

  @Inject
  private FactoryClass toBeInjected;

  private ConfigurationField passedIn;
  private SomeOtherDepIfYoudLike otherDep;

  @Inject public C(@Assisted ConfigurationField passedIn,
      SomeOtherDepIfYoudLike otherDep) {
    this.passedIn = passedIn;
    this.otherDep = otherDep;
  }
}

Now in your module:

@Override public void configure() {
  install(new FactoryModuleBuilder().build(C.Factory.class));
}

Now when someone wants to create a C, they can avoid calling the constructor directly; instead, they inject a C.Factory into which they pass a ConfigurationField instance of their choice and receive a fully-constructed, fully-injected C instance. (Like with most well-designed DI objects, they can call the constructor directly.)

Note that this design is especially useful in a few ways:

  • You can use constructor injection, treat all your fields as final, and treat the object as immutable.
  • If you stick with constructor injection entirely, your object will never be in a partially-initialized state, and your API stays simple (call the constructor and your object is ready).
  • For testing, you can write any implementation of C.Factory and have it return any instance you want. This can include test doubles of C or its factory: Fakes, mocks, or spies that you create manually or by using Mockito, EasyMock, JMock, or any other mocking framework.
like image 104
Jeff Bowman Avatar answered Sep 18 '22 02:09

Jeff Bowman