简体   繁体   中英

Is there a way to access Guice injectors which have previously been created?

Taking a look at Guice (and Dagger) for a new project. Every Guice tutorial I have seen so far shows an injector being created at the point where the developer needs the DI to create an object instance.

A typical example seen on the web:

public static void main(String[] args) {
    Injector injector = Guice.createInjector(new BasicModule());
    Communication comms = injector.getInstance(Communication.class);
}

To me this defeats the purpose of the DI - everywhere an instance is required, you tie the instance to the modules which define how to build it.

Is there a way to ask Guice to create an instance of a class who's module (dependency graph) has been previously defined to Guice (eg. at application startup?).

I am using the Dropwizard.io framework so there are situations where I don't have full control over the way a class is constructed but want to be able to mock out the dependencies I reference in that class.

Exactly the same applies to Dagger - I'd appreciate examples for either/both.

Edit: I've worked with several DI frameworks in .NET over the years, so I'm going to give an example of what I'm trying to do based on one of those.

For example, in the ASP.NET Core DI implementation, at service start-up you define the services you want the DI to be able to create. Typically you'll be asking the DI to give you an instance which is an implementation of an interface. So at start-up:

protected override void ConfigureAdditionalServices(IServiceCollection services)
{
    services.AddScoped<ITransactionService, TransactionService>();
}

where IServiceCollection is the collection of services defined to the DI.

Because the DI is integrated with the ASP.NET framework, from this point on you can generally define a constructor which takes ITransactionService and the DI will provide it for you.

However, if you were using the DI in a framework which did not know about it, you'd need access to the current ServiceProvider instance, and then you could ask the DI to create your object like this:

var transactionService = ServiceProvider.GetService<ITransactionService>();

I realise that this implemeting theService Locator anti-pattern but it still has the benefit of decoupling my code from the concrete class implementations and allowing me to mock them out for testing at application start-up.

So back to the question So to restate my question in the light of this context, how can I request a class from Guice at some random point in my code?

What would I need to change in this code to make it work?

public class TransactionModule extends AbstractModule {
  @Override 
  protected void configure() {
    bind(TransactionServiceBase.class).to(TransactionService.class);
  }
}

// At startup
Injector injector = Guice.createInjector(new TransactionModule());


// Then, somewhere in the application, to get an instance of TransactionService
TransactionServiceBase transactionService = (TransactionServiceBase)Guice.getInstance(TransactionServiceBase.class);

I think you might be misunderstanding Injector.getInstance - in the same way that your example has a public static method to start things off, even though you don't usually write the rest of your app with all public static methods (I hope), you also don't call Injector.getInstance except in a very few specific cases.

Instead, this code is just used to get things going. Another popular "get this started" is injector.injectMembers(this) - let the main() create an instance manually of whatever is the basis of your application, with @Inject -annotated members, and then just ask the now-created Injector to populate the members.

As you continue "inward" to the rest of your application, you likely will never reference the Injector instance again, but instead rely on providers, assisted inject, or just guice-created instances.

In this way, you should never care about the Injector or the exact modules that were set up for it. Only one Injector should exist over the lifetime of the app (there are essentially no exceptions to this, except perhaps if you redefine what an "app" is), and 99% of the time it is hidden from you (exceptions: where your DI meets some other DI, and needs to request an instance of something by its Class, or where you have tooling that wants to introspect the set of all declared bindings). As such, you should be able to just provide setters, constructor args, or init methods, and then can call them manually, or have any guice context create them based on their own specific modules and rules.

I think that the answer is that there is no standard/supported way to do that.

The usual assumptions is that you have as few 'injections' points as possible, ideally one, and then you specify what each class needs by making dependencies constructor arguments.

The only scenario I know well similar to what you describe here is Dagger in android apps. The way they solved it is that they store the dagger "Injector" (sorta) in a global object - the Application, and then Dagger provides a static function to retrieve that object and perform the injections.

Long story short DI frameworks don't play well with paradigms where you don't instantiate classes yourself.

And the only solution I can think of is storing your injector in some global variable and get it from there when you need it.

Static injection might help to some degree in your case https://github.com/google/guice/wiki/Injections#static-injections

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