Basic injection in Guice

We have seen a basic DI implementation, now it is time to understand how injection works in Guice. Let's rewrite the example of a notification system using Guice, and along with that, we will see several indispensable interfaces and classes in Guice.  We have a base interface called NotificationService, which is expecting a message and recipient details as arguments:

public interface NotificationService {
boolean sendNotification(String message, String recipient);
}

The SMSService concrete class is an implementation of the NotificationService interface. Here, we will apply the @Singleton annotation to the implementation class. When you consider that service objects will be made through injector classes, this annotation is furnished to allow them to understand that the service class ought to be a singleton object. Because of JSR-330 support in Guice, annotation, either from javax.inject or the com.google.inject package, can be used:

import javax.inject.Singleton;
import com.packt.guice.di.service.NotificationService;

@Singleton
public class SMSService implements NotificationService {

public boolean sendNotification(String message, String recipient) {
// Write code for sending SMS
System.out.println("SMS has been sent to " + recipient);
return true;
}

}

In the same way, we can also implement another service, such as sending notifications to a social media platform, by implementing the NotificationService interface. 

It's time to define the consumer class, where we can initialize the service class for the application. In Guice, the @Inject annotation will be used to define setter-based as well as constructor-based dependency injection. An instance of this class is utilized to send notifications by means of the accessible correspondence services. Our AppConsumer class defines setter-based injection as follows:

import javax.inject.Inject;

import com.packt.guice.di.service.NotificationService;


public class AppConsumer {

private NotificationService notificationService;

//Setter based DI
@Inject
public void setService(NotificationService service) {
this.notificationService = service;
}

public boolean sendNotification(String message, String recipient){
//Business logic
return notificationService.sendNotification(message, recipient);
}
}

Guice needs to recognize which service implementation to apply, so we should configure it with the aid of extending the AbstractModule class, and offer an implementation for the configure() method. Here is an example of an injector configuration:

import com.google.inject.AbstractModule;
import com.packt.guice.di.impl.SMSService;
import com.packt.guice.di.service.NotificationService;


public class ApplicationModule extends AbstractModule{

@Override
protected void configure() {
//bind service to implementation class
bind(NotificationService.class).to(SMSService.class);
}

}

In the previous class, the module implementation determines that an instance of SMSService is to be injected any place a NotificationService variable is determined. In the same way, we just need to define a binding for the new service implementation, if required. Binding in Guice is similar to wiring in Spring:

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.packt.guice.di.consumer.AppConsumer;
import com.packt.guice.di.injector.ApplicationModule;

public class NotificationClient {

public static void main(String[] args) {

Injector injector = Guice.createInjector(new ApplicationModule());

AppConsumer app = injector.getInstance(AppConsumer.class);

app.sendNotification("Hello", "9999999999");
}
}

In the previous program, the  Injector object is created using the Guice class's createInjector() method, by passing the ApplicationModule class's implementation object. By using the injector's getInstance() method, we can initialize the AppConsumer class. At the same time as creating the AppConsumer's objects, Guice injects the needy service class implementation (SMSService, in our case). The following is the yield of running the previous code:

SMS has been sent to Recipient :: 9999999999 with Message :: Hello

So, this is how Guice dependency injection works compared to other DI. Guice has embraced a code-first technique for dependency injection, and management of numerous XML is not required.

Let's test our client application by writing a JUnit test case. We can simply mock the service implementation of SMSService, so there is no need to implement the actual service. The MockSMSService class looks like this:

import com.packt.guice.di.service.NotificationService;

public class MockSMSService implements NotificationService {

public boolean sendNotification(String message, String recipient) {
System.out.println("In Test Service :: " + message + "Recipient :: " + recipient);
return true;
}

}

The following is the JUnit 4 test case for the client application:

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.packt.guice.di.consumer.AppConsumer;
import com.packt.guice.di.impl.MockSMSService;
import com.packt.guice.di.service.NotificationService;

public class NotificationClientTest {
private Injector injector;

@Before
public void setUp() throws Exception {
injector = Guice.createInjector(new AbstractModule() {

@Override
protected void configure() {
bind(NotificationService.class).to(MockSMSService.class);
}
});
}

@After
public void tearDown() throws Exception {
injector = null;
}

@Test
public void test() {
AppConsumer appTest = injector.getInstance(AppConsumer.class);
Assert.assertEquals(true, appTest.sendNotification("Hello There", "9898989898"));;
}

}

Take note that we are binding the MockSMSService class to NotificationService by having an anonymous class implementation of AbstractModule. This is done in the setUp() method, which runs for some time before the test methods run.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.12.156