Andrei Koch
Andrei Koch

Reputation: 1140

Lazy load of non-singleton in guice

I'm using Guice for DI in my project, and I want to have lazy initialization of non-singleton object in my class. I was trying to use Provider, but it returned new instance every time I call get() method.

public class MyClass {
  @Inject
  private Provider<Service> serviceProvider; //Service is NOT Singleton

  public void meth1() {
    Service s1 = serviceProvider.get();       
  }

  public void meth2() {
    Service s2 = serviceProvider.get();
  }
  //I want  s1 and s2 to be the same object
}

Does Guice can do it, or is there any other practices to reach that?

Upvotes: 1

Views: 1223

Answers (2)

Jeff Bowman
Jeff Bowman

Reputation: 95634

As Tavian mentioned in the comments, the best solution is Suppliers.memoize(serviceProvider::get), or some equivalent. Suppliers (and Supplier) are in the Google utility library Guava, and since Java 8, Guava's Supplier extends the Java 8 functional interface Supplier, though Java 8 doesn't supply a Suppliers equivalent built-in. The specification of serviceProvider::get is necessary to make Provider behave as a Supplier without an explicit adapter, even though they both specify exactly one method, a zero-arg T get().

Unlike Google's other DI framework, Dagger, Supplier doesn't have a Lazy class to provide the local memoization you're looking for. Though you could do the kind of "check then set" behavior that tmarwen offers in their updated answer, that strategy (as mentioned in their comment) is not thread-safe, while Suppliers.memoize is explicitly thread-safe.

Though Guava provides a lot of useful features, you may not want to add a whole new library for a single method. If you do need the thread safety without the full library, you can take a look at the Apache-licensed implementation here.

Upvotes: 1

tmarwen
tmarwen

Reputation: 16354

If you need s1 and s2 to refer to the same instance, then you just need a single instance of Service for an instance of MyClass and which resolves simply to an instance field:

public class MyClass {

  @Inject
  private Provider<Service> serviceProvider;

  private Service service;

  public void meth1() {
    if (this.service == null) this.service = this.serviceProvider.get();
    // use service 
  }

  public void meth2() {
    if (this.service == null) this.service = this.serviceProvider.get();
    // use service 
  }
}

Upvotes: 1

Related Questions