Wednesday, November 30, 2016

Practical guide to Guice - part 1

This post is not yet another post agitating for dependency injection, which demonstrates Guice usage with a main method. It does not pretend to be a Guice tutorial either. If you are new for Guice or dependency injection Guice wiki page may be helpful.
This post explains how to implement dependency injection with Guice end-to-end and shows several practical examples.

Where to start

Guice maven dependencies include:
 <properties>  
      <guice.version>3.0</guice.version>  
 </properties>  
 <dependencies>  
         ...  
      <dependency>  
           <groupId>com.google.inject</groupId>  
           <artifactId>guice</artifactId>  
           <version>${guice.version}</version>  
           <scope>provided</scope>  
      </dependency>  
      <dependency>  
        <groupId>com.google.inject.extensions</groupId>  
        <artifactId>guice-assistedinject</artifactId>  
           <version>${guice.version}</version>  
           <scope>provided</scope>  
      </dependency>            
      ...  
 </dependencies>  
The second jar guice-assistedinject is optional and is needed for advanced Guice features, which will be explained further.
The guice.version property should be set to the recent Guice release.

Guice learns, how to create objects graph from annotations and bindings, specified in the modules. In a simple case all the bindings may be done in a single module.
Class AppModule implements a module, which will encapsulate all injection bindings; meantime the method configure is empty and binding will be added later:
 public class AppModule extends AbstractModule {  
      @Override  
      protected void configure() {  
           // Bindings will be added here ...
      }  
 }  

Classes registered for injection should be created via a Guice injector. Since injector creation is a costly procedure, it should be created in the application only once. A class DIContainer encapsulates application injector creation and access:
 public final class DIContainer {
      private static final Injector injector;
      static {
          injector = Guice.createInjector(new AppModule());
      } 
      public static Injector getInjector() {
          return injector;
      }
 }

Injection binding and objects creation


Binding is possible by providing one of the injection annotations or explicitly in a module method configure.

The interface ServiceProvider is implemented by a class ServiceBean:
 public interface ServiceProvider {
      void useMe();
 }
 public class ServiceBean implements ServiceProvider {
      @Override
      public void useMe() {
           System.out.println("ServiceBean instance : " + this);   
      } 
 } 

The simplest way to bind an interface to a concrete class is with @ImplementedBy annotation:
 @ImplementedBy(ServiceBean.class)
 public interface ServiceProvider {
      void useMe();
 }
The same binding in the module is:
 public class AppModule extends AbstractModule {  
      @Override  
      protected void configure() {
           bind(ServiceProvider.class).to(ServiceBean.class); 
      }  
 }  

Field injection vs constructor injection


A concrete class binding may be done just by providing of injection annotation elsewhere in the class.

Class InjectedAnnotated has a data member service, annotated with @Inject:
 public class InjectedAnnotated {
      @Inject
      private ServiceProvider service;
      public InjectedAnnotated() {
      }
 }
Since InjectedAnnotated has injection binding, its instance may be created with the injector:
 InjectedAnnotated injected = DIContainer.getInjector().getInstance(InjectedAnnotated.class); 
Guice will take care to inject an instance of ServiceProvider according to its binding, specified in the module, and will assign the injected instance to the service data member.

Annotating for injection of class data members is called field injection. On field injection Guice takes care to inject all data members, marked with @Inject annotation.

Another injection method is the constructor injection. Let's modify InjectedAnnotated:
 public class InjectedAnnotated {
      private ServiceProvider service;
      @Inject
      public InjectedAnnotated(ServiceProvider service) {
           this.service = service;
      }
 }
The @Inject annotation above the constructor directs Guice to inject all constructor arguments prior to the constructor call. Instantiation of InjectedAnnotated with injector is the same:
 InjectedAnnotated injected = DIContainer.getInjector().getInstance(InjectedAnnotated.class); 

In both above examples the InjectedAnnotated constructor is public. An instance of the class may be created with plain new method.
In case of the field injection it is:
 InjectedAnnotated injected = new InjectedAnnotated(); 
The object injected is not initialized properly: the service field is null, because the instance was not created by Guice.

Constructor injection demands explicit creation of the constructor argument prior to call of new (or sending explicit null, which will draw attention and will prevent using of the wrong creation method).

Constructor injection advantages are owed to the constructor arguments:
- All injection dependencies are exposed in the constructor signature.
- It is very "unit testable", because it allows sending to the constructor mock arguments.
- It is more difficult to use by mistake new for instance creation.

On the other side, if a class has a lot of dependencies to be injected, field injection may be more appropriate.

Singleton injection


Singleton injection is an alternative to a singleton pattern.
A class, annotated with @Singleton, will be created by Guice only once. Any second call for injecting an instance will return the same instance.

The classes LazySingletonBean and EagerSingletonBean are Guice singletons:
 @Singleton
 public class LazySingletonBean { 
      public void useMe() {
           System.out.println("LazySingletonBean instance : " + this);   
      } 
 }
 @Singleton  
 public class EagerSingletonBean {  
      private static EagerSingletonBean instance;  
      public EagerSingletonBean() {  
           instance = this;  
      }  
      public void useMe() {  
           System.out.println("EagerSingletonBean instance : " + this);                 
      }  
      public static void just4Test(String message) {  
           if (instance !=null) {  
                System.out.println(message);  
                instance.useMe();  
           }  
           return;  
      }  
 }  
The EagerSingletonBean has the binding in the module:
 public class AppModule extends AbstractModule {  
      @Override  
      protected void configure() {
           bind(EagerSingletonBean.class).asEagerSingleton();
           bind(ServiceProvider.class).to(ServiceBean.class); 
      }  
 }  
This module binding makes the difference between two singletons.

The class MemberInjectionClient has injected members eagerSingelton and lazySingelton:
 public class MemberInjectionClient {  
      @Inject  
      private ServiceProvider service;  
      @Inject  
      private EagerSingletonBean eagerSingelton;  
      @Inject  
      private LazySingletonBean lazySingelton;  
      public MemberInjectionClient() {  
      }  
      public void use() {  
           service.useMe();  
           eagerSingelton.useMe();  
           lazySingelton.useMe();  
      }  
 }  
The eagerSingelton will be injected during the module creation. The lazySingelton will be injected during injection of the MemberInjectionClient.

Note: according to the Guice wiki all singletons are created eagerly in Stage.PRODUCTION.

The more topics in the next post.
Full sources on Git.

No comments :

About the author

My Photo
I trust only simple code and believe that code should be handsome. This is not a matter of technology, but professional approach, consolidated after years of software development. I enjoy to cause things working and feel very happy, when I manage to solve a problem.
Back to Top