Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple implementations in Guice with indirect dependency

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?

like image 859
frankadelic Avatar asked Mar 01 '26 09:03

frankadelic


1 Answers

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) {
    ...
  }
}
like image 118
Simon Nickerson Avatar answered Mar 03 '26 21:03

Simon Nickerson



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!