简体   繁体   中英

Guice injector.getInstance() - good practice?

Let's say I have two applications sharing the same library. This library contains common classes like DAOs, Utils, etc. Everything in the shared library is wired with Guice. My two apps depend on this library but do not have a direct dependency on Guice.

 ______    ______    ______
|      |  |      |  |      |
| APP1 |->| LIB  |<-| APP2 |
'------'  '------'  '------'

I currently use something like this:

static <T> Utils.getInstanceOf (Class<T> type);

which is simply a wrapper for:

injector.getInstance (Class<T> type);

But the guice docs say:

When feasible, avoid using this method, in favor of having Guice inject your dependencies ahead of time.

So what's the best way to provide dependency injection for the two apps without having to manually bind them in a Guice module?

So what's the best way to provide dependency injection for the two apps without having to manually bind them in a Guice module?

There is no such way. You either embrace Guice totally or do not use it and pass your dependencies explicitly. Well, structuring your code in such way so you never directly create class dependencies, passing them instead through a constructor, also may be called 'dependency injection', but I'm sure this is not what you meant. If you do not want to use Guice in your apps, you won't be able to get anything better than getInstance() , which is ugly, especially because you're using static wrapper.

Ideally your library should provide a module which you can install via Guice.createInjector() in your applications, or, in the other way around, the library should provide an Injector instance which you can use in your applications by using createChildInjector() and providing application-specific modules. Slight modification of this approach is passing application-specific modules to the library so they will be used to create Injector . I have recently written Guice-based API over custom servlet-like interface which didn't support any kind of DI at all using the last approach, and it is working perfectly.

It is not at all hard to use Guice in servlet or Jersey environment. The latter, for example, has out-of-the-box integration with Guice (at least, in 1.x versions). Guice servlet extension is also very good and convenient. Just try it and see for yourself.

static <T> Utils.getInstanceOf (Class<T> type);

What you have ended up with is a Service Locator .

While in a few small cases this it is acceptable for the injector to escape into other creational objects, I don't think this one of them. You have ended up with all the disadvantages of a Service Locator and all the advantages can be had by using the tool you are already using.

The "usual" pattern of using an injector is to set it up in some top-level entry point of you project (in a servlet scenario, using Guice-Servlet, this would be the GuiceServletContextListener). You may want to configure an individual injector at an entry point to some dependency and make it responsible for wiring that dependency, for the sake of modularization. If you want both, individual bindings and the bindings from the parent project, in your dependencies, than you can create a child injector that delegates to its parent if no binding is found. Guice supports this.

However, it seem strange to me that you want to set up an injector inside a dependency and use it in your main app(s). This would mean that the dependency knows about all bindings required by the main app(s). I'm not quite sure what you are trying to achieve with this approach. Is it that your two apps have the same/a very similar binding setup and you don't want to repeat it? In this case you should define a module with all that bindings configurations once (maybe in a dependency) and use it when setting up the injector at the entry points of each of your apps. That much in regard to your scenario.

To your question in general. I think its good practice to avoid explicitly passing the injector. Whenever you do this, you work against the idea of dependency injection as being a transparent project and you tie yourself to a concrete injection framework. In most cases you can avoid explicit references to the injector by using Providers and Factories .

If you have a method that needs to create a new instance of class C at runtime, bind a Provider to your class. C would be bound in the usual way, eg

public CModule extends AbstractModule {
    @Overide
    public void configure() {
        bind(C.class).to(CImpl.class);
    }
}

The class that creates C instances would look like this:

class UserOfC {
    private Provider<C> cProvider;
    ...

    @Inject
    UserOfC(Provider<C> cProvider, ...) {
        this.cProvider = cProvider;
        ...
    }

    public void doSomethingWithAC (...) {
        C myC = cProvider.get();  // not a singleton; new instance created!
        ...
    }
}

Guice supportes Provider injection free for nothing. If C is bound, you can inject a Provider as easily as you can inject an instance of C.

Additional Suggestions:

I strongly recommend that you inject all dependencies at construction, if at all possible, even if it requires writing a few more lines of code. I've used Guice for years, and have yet to need partial construction or any other advanced feature.

When I am faced with the need for partial injection, I generally write my own factory. I find it much easier to understand and debug when I write the code.

Yeah it ok to pass injector this way.

Even we have done something similar with our wicket application, so for non non-wicket pages we have simply used injector.get.inject(this) and passed in the constructor.

and it works perfect.

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM