2
votes

I need a suggestion for how to code for multiple implementations for a service using Google-guice. Below is the example

TestService testService =new TestServiceImplOne();
TestService testService =new TestServiceImplTwo();

As Guice doesn't allow binding a type to more than one implementations as the below code results in error

binderObject.bind(SomeType.class).to(ImplemenationOne.class);
binderObject.bind(SomeType.class).to(ImplemenationTwo.class);

we can solve this with named annotations as below

binder.bind(Player.class).annotatedWith(Names.named("Good")).to(GoodPlayer.class);
binder.bind(Player.class).annotatedWith(Names.named("Bad")).to(BadPlayer.class);

@Named("Good") Player goodPlayer = (Player)injector.getInstance(Player.class);
@Named("Bad") Player badPlayer = (Player)injector.getInstance(Player.class);

But the application which iam working is something like this. We are binding all the modules in the init() method and creating the injector modules:

//separate method to bind
protected void configure() {
  bind(new TypeLiteral<List<Service>>() {}).toInstance(serviceSets);
}

//separate method to inject
Injector i = Guice.createInjector(modules);

But with the above process I can just bind one implementation class to the interface (service class)

Could you please provide me a way to do this with providers. I would like to do something like this below

class  TestServiceProvider extends Provider{
// some code where it returns the  instance of impl class needed. In my case TestServiceImplOne and TestServiceImplTwo and provider returns the corresponding instance of service class
}

and bind service class with provider class. Something like this

bind(TestService.class).toProvider(TestServiceProvider.class);

I would appreciate if someone suggests a good example using providers or some other way that I can inject whatever implementation I want in the client.

Note: I am using webservices and I am not sure how I can inject different implementations when a webservice is called to a service class.


First of all thanks very much for responding . Coming straight to the point

Iam working on webservices . Heres's the Flow

// GET URI GET http://www.google.com:8182/indi/provide/organizations/{ou}

OrganizationsResource -------->OrganizationService------>OrganizationServiceImpl

Iam binding OrganizationService with OrganizationServiceImpl and injecting the OrganizationService in OrganizationsResource

@Inject
    public void setOrganizationService(OrganizationService orgService) {
        this.orgService= orgService;
    }


Its fine till here but i have two implementations for OrganizationService ------>OrgDeatilsServiceImpl which does some other job

Now i want to bind both OrganizationServiceImpl and OrgDeatilsServiceImpl to OrganizationService

Confusions:

1) What procedure i have to use in Guice to bind two implementaions?
2) How exactly i can code in OrganizationsResource  to dynamically decide which implementation to call.


I would appreciate if you give a sample example for the above requirement. 
1
It is absolutely not clear why you can't use binding annotations. Please expand on that part. And BTW your annotations example is wrong. Annotating local variables and assigning them with Injector.getInstance() call will do nothing. You'll have to use Key class for that. - Vladimir Matveev

1 Answers

4
votes

As Vladimir noted, you can use binding annotations with Providers...

// in YourModule.configure():
bind(TestService.class)
    .annotatedWith(Names.named("foo")
    .toProvider(TestServiceProvider.class);

...and generic types using TypeLiterals...

bind(new TypeLiteral<List<Service>>() {})
    .annotatedWith(Names.named("bar")
    .toInstance(serviceSets);

...as long as you ask for an annotated instance using getInstance(Key<T>)...

List<Service> servicesOne = injector.getInstance(
    new Key<List<Service>>(Names.named("bar")) {});
// or
List<Service> servicesTwo = injector.getInstance(
    Key.get(new TypeLiteral<List<Service>>() {}, Names.named("bar"));

...or, preferably, keep them as fields and let Guice do the injecting, because Guice can't inject local variables. Remember that Guice can only inject classes that it creates, or that you request specifically.

class MyInjectorCreator {
  @Inject @Named("foo") Provider<TestService> fooServiceProvider;
  @Inject @Named("bar") List<Service> barServices;
  // Guice will also wrap/unwrap Providers automatically.
  @Inject @Named("foo") TestService fooService;
  @Inject @Named("bar") Provider<List<Service>> barServicesProvider;

  public void createInjector() {
    Injector injector = Guice.createInjector(getListOfModules());
    injector.injectMembers(this);
  }
}

Now, that answers the question as you phrased it in the title. That said, it sounds like you actually want to choose between implementations at runtime, which is a slightly different but easy-to-solve problem:

class TestServiceProvider extends Provider<TestService> {
  // Injection is allowed here!
  @Inject ApplicationSettings settings;
  @Inject Provider<TestServiceImplOne> oneProvider;
  @Inject Provider<TestServiceImplTwo> twoProvider;

  @Override public TestService get() {
    if (settings.isInTestMode()) {
      return new TestTestServiceImplImpl(); // without injection!
    } else if (settings.useNewService()) {
      return twoProvider.get(); // with injection!
    } else {
      return oneProvider.get(); // also with injection!
    }
  }
}

But I should warn you that if you know at injector creation time which service to use, you should probably just bind it correctly then for the sake of code cleanliness and ease of readability:

// in YourModule.configure():
if (settings.isInTestMode()) {
  bind(TestService.class).toInstance(new TestTestServiceImplImpl());
} else if (settings.useNewService()) {
  bind(TestService.class).to(TestServiceImplTwo.class);
} else {
  bind(TestService.class).to(TestServiceImplOne.class);
}