My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
InjectingProviders  
Updated Oct 16, 2011 by sberlin

Injecting Providers

With normal dependency injection, each type gets exactly one instance of each of its dependent types. The RealBillingService gets one CreditCardProcessor and one TransactionLog. When this flexibility is necessary, Guice binds a provider. Providers produce a value when the get() method is invoked:

public interface Provider<T> {
  T get();
}

The provider's type is parameterized to differentiate a Provider<TransactionLog> from a Provider<CreditCardProcessor>. Wherever you inject a value you can inject a provider for that value.

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

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

  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
    CreditCardProcessor processor = processorProvider.get();
    TransactionLog transactionLog = transactionLogProvider.get();

    /* use the processor and transaction log here */
  }
}

For every binding, annotated or not, the injector has a built-in binding for its provider.

Providers for multiple instances

Use providers when you need multiple instances of the same type. Suppose your application saves a summary entry and a details when a pizza charge fails. With providers, you can get a new entry whenever you need one:

public class LogFileTransactionLog implements TransactionLog {

  private final Provider<LogFileEntry> logFileProvider;

  @Inject
  public LogFileTransactionLog(Provider<LogFileEntry> logFileProvider) {
    this.logFileProvider = logFileProvider;
  }

  public void logChargeResult(ChargeResult result) {
    LogFileEntry summaryEntry = logFileProvider.get();
    summaryEntry.setText("Charge " + (result.wasSuccessful() ? "success" : "failure"));
    summaryEntry.save();

    if (!result.wasSuccessful()) {
      LogFileEntry detailEntry = logFileProvider.get();
      detailEntry.setText("Failure result: " + result);
      detailEntry.save();
    }
  }

Providers for lazy loading

If you've got a dependency on a type that is particularly expensive to produce, you can use providers to defer that work. This is especially useful when you don't always need the dependency:

public class DatabaseTransactionLog implements TransactionLog {
  
  private final Provider<Connection> connectionProvider;

  @Inject
  public DatabaseTransactionLog(Provider<Connection> connectionProvider) {
    this.connectionProvider = connectionProvider;
  }

  public void logChargeResult(ChargeResult result) {
    /* only write failed charges to the database */
    if (!result.wasSuccessful()) {
      Connection connection = connectionProvider.get();
    }
  }

Providers for Mixing Scopes

It is an error to depend on an object in a narrower scope. Suppose you have a singleton transaction log that needs on the request-scoped current user. Should you inject the user directly, things break because the user changes from request to request. Since providers can produce values on-demand, they enable you to mix scopes safely:

@Singleton
public class ConsoleTransactionLog implements TransactionLog {
  
  private final AtomicInteger failureCount = new AtomicInteger();
  private final Provider<User> userProvider;

  @Inject
  public ConsoleTransactionLog(Provider<User> userProvider) {
    this.userProvider = userProvider;
  }

  public void logConnectException(UnreachableException e) {
    failureCount.incrementAndGet();
    User user = userProvider.get();
    System.out.println("Connection failed for " + user + ": " + e.getMessage());
    System.out.println("Failure count: " + failureCount.incrementAndGet());
  }
Comment by o.gro...@gmail.com, Jun 8, 2009

What if the providee has dependencies that are know only when it's time to create it?

Comment by project member limpbiz...@gmail.com, Jun 8, 2009

@o.groulx: Guice checks that dependency types exist at start-up. But it doesn't actually attempt to resolve them. Therefore, things should Just Work if your dependencies are only available when they're needed.

Comment by o.gro...@gmail.com, Jun 8, 2009

I'm not sure to understand. How Guice can figure out something like this?

//in a method somewhere User user = userRepository.getUser("john"); SomethingThatNeedsAUser s = provider.get();
Comment by dob...@gmail.com, Jul 22, 2009

I think your provider can use the Guice injector to create the object, although you should probably put the provider on a child injector so you don't accidentally create an endless recursion.

Comment by ljh552@gmail.com, Dec 23, 2009

When the privider injection happens?

I didnot see any binding for the user-defined privider. It seems that the GUICE create a provider on the run-time. How can we use our own defined privider under this circumstance?

Comment by dreamten...@gmail.com, Jan 6, 2010

If I create a provider and bind it to a class, like this

bind(MyClass.class).toProvider(MyClassProvider.class)

will Provider<MyClass> automatically be bound to MyClassProvider? In other words, would the following injection

@Inject
public Foo(Provider<MyClass> myClassProvider)

be satisfied with an instance of MyClassProvider?

Comment by gpampara@gmail.com, Jan 8, 2010

@dreamtennis: yes.

Comment by Nooonika@gmail.com, Feb 12, 2010

The guice is wanderful DI library, thanks!

Comment by u.int.3...@gmail.com, Aug 1, 2010

What if the provided type is supposed to be thread-local scoped?

It seems to me that the provider will be providing you with unscoped objects, no?

Comment by u.int.3...@gmail.com, Aug 1, 2010

Nevermind, I realized that the provider is scoped. Doh.

Comment by emaild...@gmail.com, Feb 13, 2011

I created an EntityManagerProvider?<EntityManager> to create an EntityManager? on demand for my application. Now I would like to write some tests and I`m planing to define a test database. But my EntityManagerProvider?<EntityManager> generates an EntityManager? that uses my dev base, not the test one. How can I use an EntityManagerProvider? to inject my test database during the tests?

Comment by vfunst...@gmail.com, Sep 21, 2011

This is a nitpick and unrelated to point of the scoping example, but I think you would want to rewrite the logConnectionException method in that example as follows to avoid incrementing the counter twice:

public void logConnectException(UnreachableException e) {
 User user = userProvider.get(); 
 System.out.println("Connection failed for " + user + ": " + e.getMessage());
 System.out.println("Failure count: " + failureCount.incrementAndGet()); 
} 

Sign in to add a comment
Powered by Google Project Hosting