Cheating way: Stick input
in a static variable or singleton ThreadLocal
. Set it before your pipeline starts and clear it after it ends. Bind everything else through DI.
Fancy way: In A
, refer to a @PipelineInput String inputString
but don’t bind it in your main injector. Otherwise, bind dependencies as you normally would, including referring to @PipelineInput
in other pipeline-related classes. When you do need a D
, get it from your implementation of a DFactory
, which I’m calling PipelineRunner
.
public class PipelineRunner {
@Inject Injector injector; // rarely a good idea, but necessary here
public D createD(final String inputForA) {
Module module = new AbstractModule() {
@Override public void configure() {
bindConstant(inputForA).annotatedWith(PipelineInput.class);
}
};
return injector.createChildInjector(new PipelineModule(), module)
.getInstance(D.class);
}
}
Naturally, binding attempts for A
, B
, C
, and D
will fail outside of PipelineRunner
for lack of a @PipelineInput String
–you’ll get a CreationException
when you create the injector with those unsatisfied dependencies, as you discovered–but those pipeline-based dependencies should be easy to separate into a Module that you install into the child injector.
If this feels too hacky, remember that PrivateModules are also “implemented using parent injectors“, and that the whole point of dependency injection is to make a dependency like inputForA
available to the whole object graph in a decoupled way.