My Java code uses multiple implementations of an interface (BugTemplate in the example below), where the bound implementation depends on context.
In Guice, I believe the proper way to accomplish this is with BindingAnnotations. However, my use case is different than the canonical example, in that the context is one level removed from the interface's implementation.
Summarizing the possible dependencies:
FooBugFinder -> BugFiler -> FooBugTemplate
BarBugFinder -> BugFiler -> BarBugTemplate
etc.
Example code:
class FooBugFinder {
// ...
public findBugsAndFileThem() {
List<Bug> bugs = findBugs();
bugFiler.fileBugs(bugs);
}
@Inject
FooBugFinder(BugFiler bugFiler) {
// BugFiler should have been instantiated with a FooBugTemplate.
this.bugFiler = bugFiler;
}
}
class BugFiler {
// ...
public fileBugs(List<Bug> bugs) {
List<FormattedBugReport> bugReports = bugTemplate.formatBugs(bugs);
fileBugReports(bugReports);
}
@Inject
BugFiler(BugTemplate bugTemplate) {
// The specific implementation of BugTemplate is context-dependent.
this.bugTemplate = bugTemplate;
}
}
interface BugTemplate {
List<FormattedBugReport> formatBugs(List<Bug> bugs);
}
class FooBugTemplate implements BugTemplate {
@Overrides
List<FormattedBugReport> formatBugs(List<Bug> bugs) {
// ...
}
}
My first thought is to annotate the ctor as follows:
FooBugFinder(@Foo BugFiler bugFiler) { }
However, how would Guice know to apply that annotation when injecting arguments to BugFiler's constructor?
In other words, FooBugFinder needs a BugFiler instance which was instantiated with FooBugTemplate. BarBugFinder needs a BugFiler instance which was instantiated with BarBugTemplate.
Any ideas?
You can do it by creating a private module that exposes an annotated BugFiler object:
abstract class BugFilerModule extends PrivateModule {
private final Class<? extends Annotation> annotation;
BugFilerModule(Class<? extends Annotation> annotation) {
this.annotation = annotation;
}
@Override protected void configure() {
bind(BugFiler.class).annotatedWith(annotation).to(BugFiler.class);
expose(BugFiler.class).annotatedWith(annotation);
bindBugTemplate();
}
abstract void bindBugTemplate();
}
Then, when you create your injector:
Injector injector = Guice.createInjector(
new BugFilerModule(Foo.class) {
@Override void bindBugTemplate() {
bind(BugTemplate.class).to(FooBugTemplate.class);
}
},
new BugFilerModule(Bar.class) {
@Override void bindBugTemplate() {
bind(BugTemplate.class).to(BarBugTemplate.class);
}
},
/* other modules */);
and you can create a FooBugFinder in the way you suggest:
public class FooBugFinder {
@Inject
public FooBugFinder(@Foo BugFiler fooBugFiler) {
...
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With