简体   繁体   中英

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 (eg references to factories etc) whereas some others are dynamic and created at runtime (eg 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?

What you are looking for is "On Demand" Injections:

public static void main(String[] args) 
{
    Injector injector = Guice.createInjector(...);
    CreditCardProcessor creditCardProcessor = new PayPalCreditCardProcessor();
    injector.injectMembers(creditCardProcessor);
}

or for static things

@Override public void configure() {
   requestStaticInjection(ProcessorFactory.class);
   ...
 }

All explained very well https://github.com/google/guice/wiki/Injections#on-demand-injection .

Note:

Both of these things are code smells and should only really be used for migrating old code over to Guice. New code should not use these approaches.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM