Skip to content

Injections

Googler edited this page Nov 10, 2021 · 10 revisions

Injections

How Guice initializes your objects

The dependency injection pattern separates behaviour from dependency resolution. Rather than looking up dependencies directly or from factories, the pattern recommends that dependencies are passed in. The process of setting dependencies into an object is called injection.

Constructor Injection

Constructor injection combines instantiation with injection. To use it, annotate the constructor with the @Inject annotation. This constructor should accept class dependencies as parameters. Most constructors will then assign the parameters to final fields.

public class RealBillingService implements BillingService {
  private final CreditCardProcessor processorProvider;
  private final TransactionLog transactionLogProvider;

  @Inject
  RealBillingService(CreditCardProcessor processorProvider,
      TransactionLog transactionLogProvider) {
    this.processorProvider = processorProvider;
    this.transactionLogProvider = transactionLogProvider;
  }

If your class has no @Inject-annotated constructor, Guice will use a public, no-arguments constructor if it exists. Prefer the annotation, which documents that the type participates in dependency injection.

Constructor injection works nicely with unit testing. If your class accepts all of its dependencies in a single constructor, you won't accidentally forget to set a dependency. When a new dependency is introduced, all of the calling code conveniently breaks! Fix the compile errors and you can be confident that everything is properly wired up.

Method Injection

WARNING: Method injection implies that the class being injected into is mutable, which should be avoided in general. So prefer constructor injection over method injection.

Guice can inject methods that have the @Inject annotation. Dependencies take the form of parameters, which the injector resolves before invoking the method. Injected methods may have any number of parameters, and the method name does not impact injection.

public class PayPalCreditCardProcessor implements CreditCardProcessor {

  private static final String DEFAULT_API_KEY = "development-use-only";

  private String apiKey = DEFAULT_API_KEY;

  @Inject
  public void setApiKey(@Named("PayPal API key") String apiKey) {
    this.apiKey = apiKey;
  }

Field Injection

WARNING: Same as method injection, field injection implies that the class being injected into is mutable, which should be avoided in general. So prefer constructor injection over field injection.

Guice injects fields with the @Inject annotation. This is the most concise injection, but the least testable.

public class DatabaseTransactionLogProvider implements Provider<TransactionLog> {
  @Inject Connection connection;

  public TransactionLog get() {
    return new DatabaseTransactionLog(connection);
  }
}

Avoid using field injection with final fields, which has weak semantics.

Optional Injections

TIP: Prefer OptionalBinder over @Inject(optional = true).

Occasionally it's convenient to use a dependency when it exists and to fall back to a default when it doesn't. Method and field injections may be optional, which causes Guice to silently ignore them when the dependencies aren't available. To use optional injection, apply the @Inject(optional=true) annotation:

public class PayPalCreditCardProcessor implements CreditCardProcessor {
  private static final String SANDBOX_API_KEY = "development-use-only";

  private String apiKey = SANDBOX_API_KEY;

  @Inject(optional=true)
  public void setApiKey(@Named("PayPal API key") String apiKey) {
    this.apiKey = apiKey;
  }

Mixing optional injection and just-in-time bindings may yield surprising results. For example, the following field is always injected even when Date is not explicitly bound. This is because Date has a public no-arguments constructor that is eligible for just-in-time bindings.

  @Inject(optional=true) Date launchDate;

On-demand Injection

Method and field injection can be used to initialize an existing instance. You can use the Injector.injectMembers API:

  public static void main(String[] args) {
    Injector injector = Guice.createInjector(...);

    CreditCardProcessor creditCardProcessor = new PayPalCreditCardProcessor();
    injector.injectMembers(creditCardProcessor);

Static Injections

When migrating an application from static factories to Guice, it is possible to change incrementally. Static injection is a helpful crutch here. It makes it possible for objects to partially participate in dependency injection, by gaining access to injected types without being injected themselves. Use requestStaticInjection() in a module to specify classes to be injected at injector-creation time:

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

Guice will inject class's static members that have the @Inject annotation:

class ProcessorFactory {
  @Inject static Provider<Processor> processorProvider;

  /**
   * @deprecated prefer to inject your processor instead.
   */
  @Deprecated
  public static Processor getInstance() {
    return processorProvider.get();
  }
}

Static members will not be injected at instance-injection time. This API is not recommended for general use because it suffers many of the same problems as static factories: it's clumsy to test, it makes dependencies opaque, and it relies on global state.

Automatic Injection

Guice automatically performs field and method injections on the following type of objects:

  • instances passed to toInstance() in a bind statement
  • provider instances passed to toProvider() in a bind statement

Those injections are performed as part of injector creation.

Injection Points

An injection point is a place in the code where Guice has been asked to inject a dependency.

Example injection points:

Clone this wiki locally