sol25
sol25

Reputation: 149

Instance method with Guice

I would like to have a static instance method with Guice for one of the components (non-managed bean should be able to access this class). I created something like this:

public class LookupService {

   @Inject
   private static Provider<Injector> injector = null;

   private final ILookup<IWS> lookup;

   @Inject
   public LookupService(ILookup<IWS> lookup) {
      this.lookup = lookup;
   }

   public static LookupService instance() {
     return injector.get().getInstance(LookupService.class);
   }

   public <T extends IWS> T lookup(Class<T> localInterface) {
      return lookup.lookup(localInterface);
   }

}

What do you think about this design ? Any other ideas on this ? (accessing managed beans from non-managed objects)

Upvotes: 2

Views: 1567

Answers (1)

Jeff Bowman
Jeff Bowman

Reputation: 95634

Basically, the pattern you're looking for is called "requesting static injection" and there's a Binder method dedicated to it. Once you have that down, your code looks a lot like this example from the Guice docs.

public class MainModule extends AbstractModule {
  @Override public void configure() {
    requestStaticInjection(LookupService.class);
  }
}

public class LookupService {

  /** This will be set as soon as the injector is created. */
  @Inject
  static Provider<LookupService> provider = null;

  private final ILookup<IWS> lookup;

  @Inject
  public LookupService(ILookup<IWS> lookup) {
    this.lookup = lookup;
  }

  public static LookupService instance() {
    return provider.get();
  }

  public <T extends IWS> T lookup(Class<T> localInterface) {
    return lookup.lookup(localInterface);
  }
}

A few notes:

  • While you can still set your field to be private, remember that this means you cannot set it in tests (or in future non-Guice usage) without Guice's private-field-access magic. When using injected fields, we often make them package-private and then put the tests in the same package.

  • Static injection is generally seen as something to endorse only when migrating to Guice, or when you use other code you can't change. When possible, try to avoid global state--even if this means making FooBean data-only and creating an injected FooBeanService.

  • Even though you can inject an Injector wherever you'd like, you might find it easier to test if you simply inject a Provider<LookupService> instead. Only inject an Injector if you don't know what type you're going to need until runtime--for example, if you implement LookupService.lookup(...) using an Injector by passing the class literal to the injector to get an instance.

  • In fact, it's hard to say from here, but ILookup seems to act a lot like the Service Locator pattern, which solves the exact type of problem that Guice solves with dependency injection! If that's the case, you might as well rewrite ILookup to use Guice: Just remove calls to LookupService.instance().lookup(Foo.class) and instead create a matching pair of @Inject static Provider<Foo> fooProvider and requestStaticInjection(FooUser.class).

Hope that helps!

Upvotes: 6

Related Questions