1) Introduction
Google Guice is a Dependency Injection Framework that can be used by Applications where Relation-ship/Dependency between Business Objects have to be maintained manually in the Application code. Since Guice support Java 5.0, it takes the benefit of Generics and Annotations thereby making the code type-safe. This article provides an overview about the Guice framework with a lot many samples. It then looks into the theories related to Dependency Injection Framework and the advantages of using them in Application. It also explores the various API available in Guice along with the Annotations that simplifies most of the things. The final section presents lots of samples thereby making most of the Guice API to get a much better feel towards the API.
2) Dependency Injection
Since Guice is a Dependency Injection Framework, let us make a clear understanding on Dependency Injection which is gaining more popularity in the recent years and a much needed mechanism to be followed in a typical Application. To name a few, J2EE 5.0, Spring, JBoss Seam are good examples that makes use of Dependency Injection. Now, let us take a simple example to illustrate the need for a Dependency Injection Framework.
Consider the following scenario. A Storage
represents some kind of repository for storing any type of data. If someone asks us to model the (simple) Class Diagram for this kind of situation, then we could have come up with a design very similar to the one mentioned below.
Storage.java
interface Storage{ public void store(String uniqueId, Data data); public Data retrieve(String uniqueId); }
The above interface provides a mechanism to store and retrieve Data
through its store()
and retrieve()
methods. Since Data can be stored in a Database or even a File, concrete implementations for the above interface may look something like the following.
FileStorage.java
class FileStorage implements Storage{ public void store(String uniqueId, Data data){ // Store the object in a file using Java Serialization mechanism. } public Data retrieve(String uniqueId){ // Code to retrieve the object. } }
The FileStorage
implementation class may store and retrieve Data in a file which is present in a hard disk. Following is another implementation for the Storage
interface where every information is stored in a Database.
DatabaseStorage.java
class DatabaseStorage implements Storage{ public void store(String uniqueId, Data data){ // Open a connection and store the data. } public Data retrieve(String uniqueId){ // Get the data from the Database. } }
Now, let us look into a Sample Client Application that makes use of this Client. Following is the Client code snippet which initially makes use of FileStorage
implementation and then switches over to DatabaseStorage
implementation.
StorageClient.java
public class StorageClient { public static void main(String[] args) { // Making use of file storage. Storage storage = new FileStorage(); storage.store("123", new Data()); // Making use of the database. storage = new DatabaseStorage(); storage.store("456", new Data()); } }
Note the code in the Client module carefully. Even though, the interface and the implementation classes enjoys loose coupling, the Client module has to manually create instances to the actual implementation classes. Also the relation-ship between the interface and the implementation classes is maintained directly in the Client code. Since, in most of the cases, during the compilation time itself, the Client Application knows to which implementation classes the corresponding interfaces will bound to, will it be useful if someone takes care of maintaining everything. That’s what Google Guice does. It takes creating instances in the form of Services from the Application client code and the Dependency between the Clients to its Services is automatically injected through some easy Configuration Mechanism. Following section will provide a simple example that makes use of Guice Framework.
3) Writing the first simple Guice Example
In this very simple example, let us see how Guice simplifies Development life in maintaining the Relation-ship/Dependency between objects. Let us have a look into the following code snippet. Following is the code for the traditional Add
interface which defines a method called add()
which is to be given implementation by some other class.
Add.java
package add.service; public interface Add { public int add(int a, int b); }
This is the class implementing the Add
interface which merely returns the sum of two numbers.
SimpleAdd.java
package add.service; public class SimpleAdd implements Add{ public int add(int a, int b) { return a + b; } }
This is the Module class which makes use of Guice API to establish Bindings in an Application. Theories towards Module and Bindings are covered in detail in the subsequent sections. For now, just consider that a Module can be configured with some of Bindings which is done through the Binder class. In Guice terms, a Binding refers to the process of providing association to an Interface to its original Implementation.
AddModule.java
package add.service; import com.google.inject.Binder; import com.google.inject.Module; public class AddModule implements Module{ public void configure(Binder binder) { binder.bind(Add.class).to(SimpleAdd.class); } }
In the above code, we are telling Guice to bind the implementation for Add
interface to SimpleAdd
class which literally tells that calls on Add.add()
made by the clients will be re-directed to SimpleAdd.add()
. Following is the Client code which uses this Add
interface.
AddClient.java
package add.service; import com.google.inject.Guice; import com.google.inject.Injector; public class AddClient { public static void main(String[] args) { Injector injector = Guice.createInjector(new AddModule()); Add add = injector.getInstance(Add.class); System.out.println(add.add(10, 54)); } }
More theories towards Injector, Guice are yet to come in the subsequent sections. Injector.getInstance(Add.class)
will now create and return an instance of type SimpleAdd
. Note the concrete implementation class gets bound in the AddModule.configure()
method.
4) Exploring the Guice API
Let us explore the various API that makes up the Dependency Injection in Guice. More specifically the following Interfaces/Classes are covered in this section.
- Binder
- Injector
- Module
- Guice
4.1) Binder
This interface mainly consists of information related to Bindings. A Binding refers a mapping for an Interface to its corresponding Implementation. For example, considering the above example, we refer that the interface Add
is bound to SimpleAdd
implementation.
Programatically, it is can be mentioned as follows. Note that the class object for both the interface and the implementation classes is passed on to the bind()
and the to()
methods.
binder.bind(Add.class).to(SimpleAdd.class)
It is also possible to bound an Interface directly to its Instance as the following code suggest that.
binder.bind(Add.class).to(new SimpleAdd())
The third variation is to bind the Interface to its corresponding Provider
class. By default, it is the Guice Framework which will instantiate and return objects needed by the Application. But, what if the Object Creation Process needed customization? Providers simply do that. Simply put, Providers follows the traditional Factory Pattern in creating objects. For example, consider the following code snippet,
binder.bind(Add.class).to(new AddProvider())
We will provide you samples on how to create Provider objects. For the time being, just keep a note that, in some way the AddProvider
class provides Factory methods that will return objects of type Add
.
It is also possible to bind an Interface to multiple implementations which will be covered in the later sections.
4.2) Injector
Injectors take care of creating and maintaining Objects that are used by the Clients. Injectors do maintain a set of Default Bindings from where they can take the Configuration information of creating and maintaining Relation-ship between Objects. Consider the following code snippet which will return some implementation of type Add.
Add addObject = injector.getInstance(Add.class)
To get all the Bindings associated with the Injector, simply make a call to Injector.getBindings()
method which will return a Map of Binding objects.
Map<Key, Binding> allBindings = injector.getBindings()
Note that every single Binding often has a corresponding Key object which is internally created and maintained by Guice. Providers, if any, which are associated with the Injector
can be retrieved by the following method.
Provider provider = injector.getProvider(SomeType.class)
4.3) Module
Modules are objects which will maintain the set of Bindings. It is possible to have multiple Modules in an Application. Injectors, in turn, will interact will the Modules to get the possible Bindings. Module is represented by an interface with a method called Module.configure()
which should be overridden by the Application to populate the Bindings. To simplify things, there is a class called AbstractModule
which directly extends the Module interface. So Applications can depend on AbstractModule
rather than Module
.
Consider the following code snippet,
MyModule.java
class MyModule extends AbstractModule{ public void configure(Binder binder){ // Code that binds information using the various // flavours of bind method. } }
4.4) Guice
Guice is a class which Clients directly depends upon to interact with other Objects. The Relation-ship between Injector
and the various modules is established through this class. For example consider the following code snippet,
MyModule module = new MyModule(); Injector injector = Guice.createInjector(module);
Note that Guice.createInjector()
method is passed with a Module
Object. Module class must have an overridden method called configure()
which is passed with a Default Binder object. This Binder object populates the various Bindings (to Classes, Objects and Providers) that are specific to an Application. Now when a Client requests for an Instance by invoking the getInstance()
of the Injector
class, Injector in turn will look into the various Bindings maintained by the Binder object to get the original object.
5) Guice Annotations
Guice comes with a small set of useful Annotations that are used to add meta-data values to an Application. Following are the Annotations that are going to get covered in this section.
- Implemented By
- Inject
- Provided By
- Singleton
5.1) Implemented By
This Annotation points to a Class object that provides Implementation to the interface. For example if the Add
interface has multiple implementations and if we wish to have SimpleAdd
as the default implementation, then we can say like the following,
Add.java
@ImplementedBy(SimpleAdd.class) interface Add{ public int add(int a, int b); }
5.2) Inject
For injecting instances directly to the Client code, we can use this Inject Annotation. This annotation can be used in a Constructor for a class, for a method or for a Field. For example, consider the following example,
Client.java
class Client{ @Inject public Client(MyService service){ } }
The above is an example for Constructor-level Injection assuming that the concrete implementation for the MyService interface would have got bounded by defining them in some Application specific Module. The same applies for Method-level and Field-level Annotations.
5.3) Provided By
Assuming that we want to customize the Object creation process for some interface type, then we would depend on Guice Provider mechanism. Let’s say, for Add
interface we want AddProvider
to create and return objects of type SimpleAdd
. In such a case, we can qualify what is the Provider
type for the Interface by directly annotating it in the Interface declaration. Consider the following code snippet,
Add.java
@ProvidedBy(AddProvider.class) public interface Add{ }
5.4) Singleton
By default, when a Client request for Objects by calling Injector.getInstance()
multiple times, every new instance is created and returned. If we want to restrict this number by returning one and only instance for the class (which is the Singleton Pattern), the implementation classes can be marked with Singleton Annotation.
MyConnection.java
@Singleton public class MyConnection{ public void connect(){ } public void disconnect(){ } }
6) Samples
6.1) Introduction
This section will provide you plenty of sample Applications that makes use of various Guice API. Let us explore them in much detail.
6.2) Simple Example
This simple example demonstrates the usage of Guice where we don’t have a separated Interface and Implementation. All we have is a concrete Implementation class and Clients directly depend on them. Though Guice doesn’t do much here, it is simply provided for reference. Consider the following Player
class,
Player.java
package simple; public class Player { public String name; public Player(){ } public String toString(){ return name; } }
Following is the example Client that makes use of the Player class. Note that we haven’t passed anything of type Module to the Guice.createInjector()
method simply because we don’t want anything to be get bound (interface to implementation) in the Application Code.
PlayerTest.java
package simple; import com.google.inject.Guice; import com.google.inject.Injector; public class PlayerTest { public static void main(String[] args) { Injector injector = Guice.createInjector(); Player player = injector.getInstance(Player.class); player.name = "David Boon"; System.out.println(player); } }
6.3) Resolving Multiple Dependencies
In this section, let us see how to resolve multiple Dependencies by making use of @Inject Annotation. Let’s say that we have an Object which is directly referring two or more Objects. For this simplest case, let the scenario be, ‘A person owns a Laptop and a Mobile’.
Following is the code for both Mobile and Laptop classes.
Laptop.java
package multipledepenedencies; public class Laptop { private String model; private String price; public Laptop(){ this.model = "HP 323233232"; this.price = "$545034"; } public String toString(){ return "[Laptop: " + model + "," + price + "]"; } }
Mobile.java
package multipledepenedencies; public class Mobile { private String number; public Mobile(){ this.number = "988438434"; } public String toString(){ return "[Mobile: " + number + "]"; } }
Following is the code for the Person
class which directly references Laptop
and Mobile
object. Note the use of @Inject Annotation used on the constructor.
Person.java
package multipledepenedencies; import com.google.inject.Inject; public class Person { private Mobile mobile; private Laptop laptop; @Inject public Person(Mobile mobile, Laptop laptop){ this.mobile = mobile; this.laptop = laptop; } public void diplayInfo(){ System.out.println("Mobile:" + mobile); System.out.println("Laptop:" + laptop); } }
Following is the Client Code that makes use of this sample. Since we don’t have anything to do with Bindings, we didn’t pass any Module
object in the Guice.createInjector()
method.
MultipleDependencyTest.java
package multipledepenedencies; import com.google.inject.Guice; import com.google.inject.Injector; public class MultipleDependencyTest { public static void main(String[] args) { Injector injector = Guice.createInjector(); Person person = injector.getInstance(Person.class); person.diplayInfo(); } }
The above program will output something like the following,
Mobile:[Mobile: 988438434] Laptop:[Laptop: HP 323233232,$545034]
6.4) Making use of Binding Annotation
In Guice, it is not possible to binding a type to more than one implementations. For example, the following code would result in a Runtime Error.
binderObject.bind(SomeType.class).to(ImplemenationOne.class); binderObject.bind(SomeType.class).to(ImplemenationTwo.class);
The following would be consider as an Error in Guice as the SomeType is bounded to more than one implementation classes. This is an Error simply because Guice don’t know which Instance to be returned upon Client’s Request. But in language like Java, it is perfectly legal to have multiple implementations for some type T with the concept of interfaces. One way to achieve this way in Guice is to depend on Binding Annotations. For example, consider the Player
interface,
Player.java
package playerservice; public interface Player { public void bat(); public void bowl(); }
Following are the implementations for the Player
interface, GoodPlayer
and BadPlayer
.
GoodPlayer.java
package playerservice; public class GoodPlayer implements Player{ public void bat() { System.out.println("I can hit any ball"); } public void bowl() { System.out.println("I can also bowl"); } }
BadPlayer.java
package playerservice; public class BadPlayer implements Player{ public void bat() { System.out.println("I think i can face the ball"); } public void bowl() { System.out.println("I dont know bowling"); } }
Now we want to instruct to Guice that for the Player
interface, we have multiple implementations in the form of GoodPlayer
and BadPlayer
. Anyway, finally the Client makes use of one of the concrete Implementations only. Either they will use an implementation of type Good Player or Bad Player. Through some Annotation mechanisms we are now going to instruct Guice about the multiple Implementations. Following is the code for that,
PlayerModule.java
package playerservice; import com.google.inject.*; public class PlayerModule implements Module{ public void configure(Binder binder) { binder.bind(Player.class).annotatedWith(Good.class).to( GoodPlayer.class); binder.bind(Player.class).annotatedWith(Bad.class).to( BadPlayer.class); } }
We have used two custom Annotation types Good
and Bad
. The above code essentially tells to bind the implementation to GoodPlayer
if it is annotated with Good
and bind the Player type to BadPlayer
if it is annotated with Bad
. Following is definition for both the Good
and the Bad
annotations.
Good.java
package playerservice; import java.lang.annotation.*; import com.google.inject.BindingAnnotation; @Retention(RetentionPolicy.RUNTIME) @BindingAnnotation @Target(ElementType. LOCAL_VARIABLE) public @interface Good {}
Bad.java
package playerservice; import java.lang.annotation.*; import com.google.inject.BindingAnnotation; @Retention(RetentionPolicy.RUNTIME) @BindingAnnotation @Target(ElementType. LOCAL_VARIABLE) public @interface Bad {}
Following is the Client code for the above Program. Note that when requesting for a particular implementation the Client directly specifies the Annotation on the local reference thereby making the Guice to identify the implementation class. The code @Good
Player player essentially tells Guice that a concrete implementation of type GoodPlayer
has to be injected into the player reference because that is how we have configured in the Player
Module.
PlayerClient.java
package playerservice; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; public class PlayerClient { public static void main(String[] args) { PlayerModule module = new PlayerModule(); Injector injector = Guice.createInjector(new Module[]{module}); @Good Player player = (Player)injector.getInstance(Player.class); player.bat(); player.bowl(); } }
6.5) Named Annotations
Creating new Annotation types for every concrete implementation doesn’t provide to be much useful as the sole purpose of having such an Annotation is just to mark the Implementation class instance needed by the Clients. For that we have @Named annotation which can be used to name entities. And there is an utility method called Names.named()
which when given a name returns the Named
Annotation. For example, in the above Player Module can be replaced with something similar to the following,
PlayerModule.java
package playerservice; import com.google.inject.Binder; import com.google.inject.Module; public class PlayerModule implements Module{ public void configure(Binder binder) { binder.bind(Player.class).annotatedWith(Names.named("Good")).to( GoodPlayer.class); binder.bind(Player.class).annotatedWith(Names.named("Bad")).to( BadPlayer.class); } }
Names.named("SomeName")
will simply return an Annotation of type @Named
encapsulated with the given name. Now, back in the Client code, we should use the @Named
annotation along with the name. For example,
@Named("Good") Player goodPlayer = (Player)injector.getInstance(Player.class); @Named("Bad") Player badPlayer = (Player)injector.getInstance(Player.class);
6.6) Writing a Simple Provider
Providers are Guice simply acts as Factories in creating and returning Objects. In most of the cases, Clients can directly depend on the Guice Framework for creating Objects for the Services that they are referring. Rarely, the Application code wants to customize the Object creation process for a particular type so as to control the number of objects created, to provide cache mechanism and so on. For that we have to depend on Guice Provider
classes.
For example, let us say that we want to create the Object creation and maintenance process for MockConnection
class which is given below.
MockConnection.java
package named; public class MockConnection { public void connect(){ System.out.println("Connecting to the mock database"); } public void disConnect(){ System.out.println("Dis-connecting from the mock database"); } }
Now let us write a simple Provider
class that conforms to Guice Provider that creates and return MockConnection
objects. Following is the code for the same.
ConnectionProvider.java
package named; import com.google.inject.Provider; public class ConnectionProvider implements Provider{ @Override public MockConnection get() { // Do some customization mechanism here. MockConnection connection = new MockConnection(); // Do some customization mechanism here too. return connection; } }
Note that all custom Provider class must implement the Provider
Interface and should override the get()
method to return the objects created in some custom fashion. Now, the module should be aware of the Custom Provider class that so Guice can request the ConnectionProvider
to create instances instead of creating them on its own. Following is the Module code along with the Client code.
ConnectionTest.java
package named; import com.google.inject.*; public class ConnectionTest { public static void main(String args[]){ Injector injector = Guice.createInjector( new Module(){ @Override public void configure(Binder binder) { binder.bind(MockConnection.class).toProvider( ConnectionProvider.class); } } ); MockConnection connection = injector.getInstance(MockConnection.class); connection.connect(); connection.disConnect(); } }
Note that in the Module.configure()
method, we have bound the type MockConnection.class
to a Provider by calling the method Binder.toProvider()
.
6) Conclusion
This article started off with the need to have a Dependency Injection Framework like Guice. Though there are so many Dependency Injection Frameworks coming these days, usage of Guice is simple and the API is also small. There is no need to maintain a separate XML file as all the Configuration related information is nicely encapsulated by means of Module mechanism. In this article the Core API along with the Guice Annotations was explored followed with plenty of Samples.