P Li
P Li

Reputation: 5212

How to use guice injection for an existing singleton class?

I have an existing class named Legacy which is mostly written in old school singleton pattern. Now I want to introduce a new field to it and I would like to use Guice. Legacy itself is not Guice controlled, it is used by another Service class (inside the Service class, it calls the getInstance() of Legacy class to retrieve the Legacy object right now), and that Service class is been created using Guice injector.

public class Legacy {

   public synchronized static Legacy getInstance() {
       if(sInstance == null) {
           sInstance = new Legacy();
       }
       return sInstance;
   }

   private Legacy() {
       legacyObj = LegacyField.getInstance(); // get a singleton
   }

   private static Legacy sInstance;

   private LegacyField legacyObj;

   private NewField newObj; // this is the new dependency I would like to add using Guice
}

What I tried is that I tried to put method Inject into Legacy class

@Inject
public void setNewField(NewField newObj) {
  this.newObj = newObj;
}

And in the module file of the Service, I bind the NewField object, but when I run the program, it throwed a NullPointer exception. So the inject doesn't work. Any idea of how to make NewField inject into my program but keep the current old-school singleton paradigm and not changing too much about everything else?

EDIT There are a least three solutions below and I don't quite know which is the best or are they equivalent.

Upvotes: 3

Views: 4748

Answers (3)

P Li
P Li

Reputation: 5212

I just found another solution:

// put in the module
bind(Legacy.class).toInstance(Legacy.getInstance());

In this example, your module itself, not Guice, takes responsibility for obtaining a Legacy instance, then asks Guice to always use this single instance to fulfill all Legacy injection requests. But according to the javadoc When the Injector is created, it will automatically perform field and method injection for this instance, but any injectable constructor on Legacy is simply ignored. Note that using this approach results in "eager loading" behavior that you can't control.

Upvotes: 3

Jeff Bowman
Jeff Bowman

Reputation: 95614

Though only slightly cleaner than Thomas's answer, you can configure the injection of your Singleton from within your Module using requestInjection or requestStaticInjection.

// In your Module:
requestInjection(Legacy.getInstance());  // for an instance field, or
requestStaticInjection(Legacy.class);    // for a static field.

The docs on the wiki warn about the downsides, though:

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.

Upvotes: 2

Thomas Fritsch
Thomas Fritsch

Reputation: 10127

Here is a somewhat hackish solution.

In the bootstrapping of your application, may be in method public static void main(String[] args), you should already have code similar to this:

Injector injector = Guice.createInjector(yourModule);

At this place add the following line:

injector.injectMembers(Legacy.getInstance());

By doing so, all the @Injects in your Legacy singleton should be resolved.
See also the javadoc of Injector.injectMembers.

Upvotes: 1

Related Questions