Alex M
Alex M

Reputation: 395

How to inject Injector?

Situation: i need lazy dependency instantiation in some FooClass, so i pass Injector to class as a constructor parameter.

private final Injector m_injector;
    
public FooClass(@Named("FooInjector") Injector injector) {
    m_injector = injector;
}

But guice doesn't permit to bind core classes (injectors, modules and etc). What is the solution?

Upvotes: 32

Views: 25834

Answers (4)

David Noha
David Noha

Reputation: 670

As others have already answered, you can simply use @Inject Injector because Guice defines the binding itself.

Normally you only need one Injector in your app, and a static variable is an even easier way to store and access a singleton than injecting it. In our web app, we use stripes-guicer and get the Injector from its static method GuiceInjectorFactory.getInjector() when we need it (in our Hibernate interceptor, for example).

I'm a little baffled by the advice that "you shouldn't use Injector directly." How else would I get an instance injected except by calling injector.getInstance() or injector.injectMembers()? There is no way. Yes, you can define Provider methods, but they will never be called unless somewhere, something uses an Injector. Yes, there are modules that use the Injector for you like the ServletModule; you have to create the Injector yourself, but you can leave it to the ServletModule after that.

So in some circumstances, you can avoid using the Injector directly, but that doesn't mean you "shouldn't" use it. If you're using Guice alone without any optional modules, then you "should" be using an Injector all over the place because there's no other way to trigger injection. (I think developers who spend all day writing code inside frameworks sometimes forget that some people actually instantiate their own objects.)

Upvotes: 15

Drew Stephens
Drew Stephens

Reputation: 17827

The arguments that you probably shouldn't be injecting an instance of Injector are quite valid, but as with any rule there are exceptions.

I have a factory class that takes in class references for which it needs to provide an instance. The instances aren't necessarily known (really, they are, but there are a lot and there might be more) so I can't make Providers for all of them.

public class ThingFactory {
    private Injector injector;

    @Inject
    ThingFactory(Injector injector) {
        this.injector = injector;
    }

    public <T> T getInstance(Class<T> aClass) {
        return injector.getInstance(aClass);
    }
}

The real class in my app is extending and overriding another class—that's why this class is basically a passthrough to Guice.

Upvotes: 3

ColinD
ColinD

Reputation: 110054

As @gpampara said, Provider<T> should be used for lazy/optional initialization. Also, as I said in my answer to your other question, you should be avoiding references to the Injector in your code in almost all cases.

That said, in a class that is created by Guice, the Injector that is creating the object can be injected just by declaring a dependency on Injector. The Injector is automatically available for injection without you declaring any binding for it.

If you do inject the Injector, you should think about WHY you want to do that. Why don't you just declare dependencies on the actual interfaces/classes the class depends on? It's just as easy to add a new dependency to the constructor as it is to retrieve an instance of some dependency through the Injector elsewhere in your code, and it makes the code far more understandable as well.

Upvotes: 8

gpampara
gpampara

Reputation: 12049

You should not be using the Injector directly. Rather pass in the Provider<FooClass> instead. Also, you should be injecting the provider in the places where you use FooClass.

private final Provider<FooClass> provider;

@Inject
public ClassWhereFooIsUsed(Provider<FooClass> provider) {
    this.provider = provider;
}

.... somewhere else
FooClass f = provider.get(); // This is lazy

Upvotes: 29

Related Questions