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