Java Dependency Injection – Using Guice to Replace Service Locator Implementation

dependency-injectionjavaservice-locator

Consider I have a service called FileSystem, and that this FileSystem is used by various classes throughout the application. Typically, the service is acquired via some static class method ServiceLocator.getService(FileSystem.class)

Now consider I have a class Game, that depends on the FileSystem object to open up some UI resources. The game will also construct a world, using a WorldFactory. The WorldFactory also requires the FileSystem service.

Here is where I encounter the problem, I construct a new instance of WorldFactory – which requires an instance of FileSystem to be injected into it. However, Guice will not inject into objects it has not constructed. So WorldFactory is left without a FileSystem injected into it.

So I could pass a FileSystem object to the WorldFactory but then in essence Guice hasn't solved any of my problems and I have come to realize I haven't been using Guice correctly because I can do all of this without it?

So then you get an odd instance where the constructor of a game entity requires that you pass an instance to the AudioSystem to it which it can play audio through. It just gets very messy with all these objects/services being explicitly passed around.

Thanks. I'm still new to guice and trying to figure out how to use it.

Best Answer

Both Game and WorldFactory class would have FileSystem as their dependencies. Below is an example of dependency injected through a constructor (it is also possible to inject them directly into fields):

@Inject
public Game(FileSystem fs) { }

@Inject
public WorldFactory(FileSystem fs) { }

You could then write a provider inside one of your modules that would construct and return a new FileSystem instance:

@Provides
FileSystem provideFileSystem() {
    return ServiceLocator.getService(FileSystem.class);
}

Once you treated other services similarly, you will be able to completely remove your reliance on ServiceLocator, moving the service-creating code directly into providers.

So then you get an odd instance where the constructor of a game entity requires that you pass an instance to the AudioSystem to it which it can play audio through. It just gets very messy with all these objects/services being explicitly passed around.

Not only this is not messy, this is the whole point of dependency injection. The thing is, however, that you should never explicitly pass all those instances manually. Instead, you can have Guice reconstruct the dependency graph from them and construct all necessary, intermediate objects (or obtain them in any other way you specify in your providers). On your side, creating the final object is just a matter of calling Injector.getInstance(Foo.class) -- it's up to Guice to figure out how to satisify the dependencies.