The Guice guidance on side-effects in modules is basically "don't do it." Same with doing I/O in Providers, which I assume includes modules. They then give a vacant example of using a Service interface that starts their services in the proper order.
Injector injector = Guice.createInjector(
new DatabaseModule(),
new WebserverModule(),
...
);
Service databaseConnectionPool = injector.getInstance(
Key.get(Service.class, DatabaseService.class));
databaseConnectionPool.start();
addShutdownHook(databaseConnectionPool);
Service webserver = injector.getInstance(
Key.get(Service.class, WebserverService.class));
webserver.start();
addShutdownHook(webserver);
But in the real world, there the webserver transitively depends on the database. What's more, the only way to get a DataSource is to connect to the database. And in between the webserver and the database are several layers of abstraction, all wanting their dependencies injected AND having their services start.
I've considered two different options to handle this situation. First is to go with guice's services, but inject Provider<DataSource>
s or equivalents. But this would leak guice all over my code because everything now uses Provider<LayerBelow>
instead of the actual LayerBelow
The second approach I tried was to chain creating injectors where I build a child injector per layer, start the layer, then use that to create a child injector for the next. It also seems kludgy. Like this:
// I/O in the static method call; returns a module with
// the environment-based objects
EnvironmentModule environment = EnvironmentModule.configureEnvironment(args);
Injector injector = Guice.createInjector(
environment,
// the following connects to the database, and returns a module
// providing the DataSource
SQLModule.connectToDatabase());
injector = injector.createChildInjector(new BusinessLayerModule());
startTimers(injector);
injector = injector.createChildInjector(
new ServicesModule(),
new WebServerModule());
startServer(injector);
Is there a better way to do I/O and side-effects during startup? I'm using Guice 4
Related questions:
- Call init after instantiating an object; doesn't address where constructing the object has side effects, like DataSource, though I could use it for the start-up portion.