Guice Inject Field in class not created by Guice

Verhogen picture Verhogen · Aug 4, 2010 · Viewed 10k times · Source

I have a class like so, that I create myself somewhere in my code:

class StarryEyes {
   @Inject MyValidator validator;

   public StarryEyes(String name) {
      //..
   }

   public doSomething() {
      // validator is NULL
   }
}

I want Guice to inject an instance of validator, which has a @Singleton annotation. I have a module that's loaded at startup and it contains the line:

bind(MyValidator.class);

However, it doesn't seem to work as "validator" is always null. I've tried a number of variations like:

bind(MyValidator.class)toInstance(new MyValidator());

or other things like that. Is this not how Guice is supposed to work?

Answer

ColinD picture ColinD · Aug 4, 2010

Typically Guice needs to create objects to inject them. If you just call new StarryEyes(name), Guice isn't ever going to see that object so it won't be able to inject it. One thing you can do is to call injector.injectMembers(obj) on the object after you've created it. I wouldn't recommend that, though, as you should avoid referencing the injector in your code.

What you really probably want here is Assisted Inject. With Assisted Inject, you'd declare the constructor for your class something like this:

@Inject public StarryEyes(MyValidator validator, @Assisted String name)

What that means is that validator is a parameter that Guice should inject, while name must be "assisted" (that is, provided at the time the instance is created).

You then create an interface like this:

public interface StarryEyesFactory {
  StarryEyes create(String name);
}

With Assisted Inject, Guice can then implement that factory for you. You bind it like this:

bind(StarryEyesFactory.class).toProvider(
    FactoryProvider.newFactory(StarryEyesFactory.class, StarryEyes.class));

You then inject a StarryEyesFactory anywhere you want to create an instance of it. Where you would have called new StarryEyes(name) previously, you now call starryEyesFactory.create(name) instead. When you call create(name) on the factory, it will take the name and pass it to the constructor and provide the bound validator itself.

Starting in Guice 3, you do this using a FactoryModuleBuilder:

install(new FactoryModuleBuilder().build(StarryEyesFactory.class));