How to Resolve Multiple Dependency in Google Guice API - Java @ Desk

Monday, December 15, 2014

How to Resolve Multiple Dependency in Google Guice API

How to Resolve Multiple Dependency in Google Guice API

As we have seen in previous post about how to Set up Dependency Injection using Google Guice API with Example. It is very easy to set up Dependency Injection using Google Guice as we don’t need any configuration xml unlike popular Spring Framework. As per the Concept we bind the Interface with Implementation in AbstractModule, but what if one interface has multiple implementation and we want Google Guice to resolve the Multiple Dependency and Inject the required Object at runtime.

In this post we will see how to overcome this problem. Let’s see the component one by one.


Configure Module to Enable the Dependency Injection :

To bind the interface with Implementation we need to create the Module. Guice provides two ways to resolve the Multiple Dependency -
1) Using Annotation.
2) Using Named Annotation (String)

1) Using Annotation

1. As shown below we need to create the SampleModule.Java and specify the binding for Interface to implementation through annotation.
public class SampleModule extends AbstractModule {

 // Bind interface with Implementation in Configure Method.  
 @Override
 protected void configure() {

  //Bind WebService to JMSServiceImpl with annotation JMS
  bind(WebService.class).annotatedWith(JMS.class).to(JMSServiceImpl.class);

  //Bind WebService to JMSServiceImpl with annotation REST
  bind(WebService.class).annotatedWith(REST.class).to(RESTServiceImpl.class);

  // Bind Clinet to RESTFul WebService with Annotation REST.
  bind(Client.class).annotatedWith(REST.class).to(RESTClient.class);

  // Bind Client to JMS Service with Annotation JMS.
  bind(Client.class).annotatedWith(JMS.class).to(JMSClient.class);
 } 
}


As you can see we have created Two Annotation JMS.java and REST.Java and bind the Interface with Implementation via Annotation for example Client Interface will be bind to RESTClient and RESTServiceImpl when marked with REST annotation.
Following is the definition for the Both JMS and REST Annotation –
import com.google.inject.BindingAnnotation;

@Retention(RetentionPolicy.RUNTIME)
@BindingAnnotation
@Target({ElementType.FIELD,ElementType.CONSTRUCTOR,ElementType.METHOD})
// Annotation can be Applied on Field, Constructor and Method
public @interface JMS{}


import com.google.inject.BindingAnnotation;

@Retention(RetentionPolicy.RUNTIME)
@BindingAnnotation
@Target({ElementType.FIELD,ElementType.CONSTRUCTOR,ElementType.METHOD})
// Annotation can be Applied on Field, Constructor and Method
public @interface REST{}


2. Interfaces and Implementation – As shown in the above point we need to Create the Interface and Corresponding Implementation. For Example, JSMClient and RESTClient are the type of Client which can access the Service ( JMSService / RESTService).
public interface Client {
        String accessService();
}
@Singleton
public class RESTClient implements Client {

 @Inject
 @REST
 WebService restService;
 
 @Override
 public String accessService() {
  System.out.println("Access REST Client....");
  return restService.execute();
 }
}

As you can notice Guice will Inject restService as per binding defined using REST Annotation for interface WebService.

@Singleton
public class JMSClient implements Client {

 @Inject
 @JMS
 private WebService jmsService;
 
 @Override
 public String accessService() {
  System.out.println("Access JMS Client....");
  return jmsService.execute();
 }

}

As you can notice Guice will Inject restService as per binding defined using JMS Annotation for interface WebService.

3. Services and Implementation – As shown in SampleModule following are the Service and Corresponding Implementation –
public interface WebService {

 String execute();
} 


@Singleton
public class JMSServiceImpl implements WebService{

 @Override
 public String execute() {
  System.out.println("Executing JMS Service");
  return "Executing JMS Service";
 }
 
}


4. Main Application / Execute Program
As shown in the below code you can see how Guice is resolving the dependency by injecting the instance of jmsClient and restClient based on the Annotation specified @JMS and @REST
public class MainApplication {

 @Inject
 @JMS
 Client jmsClient;
 
 @Inject
 @REST
 Client restClient;
 
 public String processRequest(){
  System.out.println("Inside the Main Application...");
  return jmsClient.accessService()+"  "+restClient.accessService();  
  }
}





2) Using Named Annotation
Creating new Annotation types for every concrete implementation might not be useful as the sole purpose of having such an Annotation is just to mark the Implementation class instance needed by the Clients. Instead of Creating our own Annotation, we can use @Named annotation which can be use to provide the name to the binding.
There is one utility method Names.named() which returns @Named annotation. This can be use to mark the Implementation with @Named annotation.

bind(WebService.class).annotatedWith(Names.named("REST")).to(RESTServiceImpl.class);



1. Sample Module for the Named Annotation
public class SampleModule extends AbstractModule {

 // Bind interface with Implementation in Configure Method.  
 @Override
 protected void configure() {

  //Bind WebService to JMSServiceImpl with annotation JMS
  bind(WebService.class).annotatedWith(Names.named("JMS")).to(JMSServiceImpl.class);

  //Bind WebService to JMSServiceImpl with annotation REST
  bind(WebService.class).annotatedWith(Names.named("REST")).to(RESTServiceImpl.class);
  
  // Bind Clinet to RESTFul WebService with Annotation REST.
         bind(Client.class).annotatedWith(Names.named("REST")).to(RESTClient.class);

  // Bind Client to JMS Service with Annotation JMS.
         bind(Client.class).annotatedWith(Names.named("JMS")).to(JMSClient.class);
 } 
}


As you can see we have used Names.named() to mark the Implementation. This approach is easy to use and handy.

2. Interfaces and Implementation
Access the Define Mapping in SampleModule using @Named Annotation as shown below.
@Singleton
public class JMSClient implements Client {

 @Inject
 @Named("JMS")
 private WebService jmsService;
 
 @Override
 public String accessService() {
  System.out.println("Access JMS Client....");
  return jmsService.execute();
 }

}
public class RESTClient implements Client {

 @Inject
 @Named("REST")
 WebService restService;
 
 @Override
 public String accessService() {
  System.out.println("Access REST Client....");
  return restService.execute();
 }
 
}


3) Services and Implementation will be same as there is no dependency Involved.

4. Inject dependency in MainApplication using Named Annotation
As shown below injected the jmsClient and restClient. Rest of the code will be same.
public class MainApplication {

 @Inject
 @Named("JMS")
 Client jmsClient;
 
 @Inject
 @Named("REST")
 Client restClient;
 
 public String processRequest(){
  System.out.println("Inside the Main Application...");
  return jmsClient.accessService()+"  "+restClient.accessService();
 }
}





Entry Point of Application
public class TestMainApplication {
 
 public static void main(String[] args) {
  
  Injector injector = Guice.createInjector(new SampleModule());
  MainApplication mainApp = injector.getInstance(MainApplication.class);
  mainApp.processRequest();
 }
}


In the Above mentioned way we can get the instance of Injector from the sameple Module. We can get the Instance of any class defined in the project using the Injector with condition either have a Default Constructor or Parameterized constructor with @Inject annotation.




How to Write JUnit test cases for Google Guice
To Write the test case for functionalities using Guice, we need to Create the instance of Injector using the Module. We can Install the SampleModule which we had created.
Following is the example to show case –
public class MainApplicationTest {

 private Injector injector;
 @Before
 public void setUp() throws Exception {
        injector = Guice.createInjector(new AbstractModule() {
             
            @Override
            protected void configure() {
             install(new SampleModule());
            }
        });
    }
 
    @After
    public void destroy() throws Exception {
        injector = null;
    }
 @Test
 public void testProcessRequest() {
  MainApplication mainApp = injector.getInstance(MainApplication.class);
  Assert.assertEquals("Executing JMS Service  Executing REST webService",mainApp.processRequest());
 }

}


Output  Following will be output after running the attached code.
START
Inside the Main Application...
Access JMS Client....
Executing JMS Service
Access REST Client....
Executing REST webService
END


This post is written by
Soumitra Pathak - Linkedin, Facebook
He is a freelance writer, loves to explore latest features in Java technology.








No comments:

Post a Comment