Enterprise Java

Guice – Google

1. Introduction

Guice is a framework that makes it easier for your application to use the dependency injection (DI) pattern. Dependency injection is a design pattern wherein classes declare their dependencies as arguments instead of creating those dependencies directly. For example, a class ‘A’ needs class ‘B’ to perform its job, so class ‘A’ does not need to worry about how it needs to build a class ‘B’ object – this should be done externally. Let us take an example where we are building an application to handle user data. We have a service class called UserService which talks to the database.

public class UserService {

    private Datastore datastore;

    public UserService() {
        this.datastore = new Datastore("/org/my-datastore");
    }
}

In the example above the UserService class is instantiating the Datastore. This makes the Datastore class tightly coupled with the UserService class. If in the future we want to change the Datastore, UserService class will need to make the change as well. This makes testing this class very hard. Instead of writing untestable or inflexible code, you can use a dependency injection pattern to address all these issues. Below is the same example but using dependency injection:

public class UserService {

    private Datastore datastore;

    public UserService(Datastore datastore) {
        this.datastore = datastore;
    }
}

The above UserService class can be used with any Datastore as UserService class has no knowledge of how the Datastore is created. For testing purposes, you can even use an in-memory database.

2. Binding

Guice uses an embedded domain-specific language, or EDSL, to help you create bindings simply and readably. This approach is great for overall usability, but it does come with a small cost: it is difficult to learn how to use the Binding EDSL by reading method-level javadocs. To bind a simple class:

bind(DefaultImpl.class);

This statement does essentially nothing; it “binds the ServiceImpl class to itself” and does not change Guice’s default behavior. You may still want to use this if you prefer your Module class to serve as an explicit manifest for the services it provides. We can bind the Service with a specific implementation:

bind(Service.class).to(ServiceImpl.class);

This specifies that a request for a Service instance with no binding annotations should be treated as if it were a request for a ServiceImpl instance. This overrides the function of any @ImplementedBy or @ProvidedBy annotations found on Service, since Guice will have already moved on to ServiceImpl before it reaches the point when it starts looking for these annotations.

bind(Service.class).toProvider(ServiceProvider.class);

In this example, ServiceProvider must extend or implement Provider. This binding specifies that Guice should resolve an unannotated injection request for Service by first resolving an instance of ServiceProvider in a regular way, then calling get() on the resulting Provider instance to obtain the Service instance.

bind(Service.class).annotatedWith(MyBinder.class).to(ServiceImpl.class);

Like the previous example, but only applies to injection requests that use the binding annotation @MyBinder

3. Example

In this section, we will see a working example. Let us create a maven project and define the dependency for google guice like below:


    com.google.inject
    guice
    5.1.0

pom.xml



    4.0.0
    org.example
    JavaCodeGeeks
    1.0-SNAPSHOT
    
        16
        16
    
    
        
            com.google.inject
            guice
            5.1.0
        
    

This will let us use the required classes for this simple example. Let us first define a very simple domain class User

User.java

package org.javacodegeeks;

public record User (String username, String address) {}

JDK 14 introduces records, which are a new kind of type declaration. Like an enum, a record is a restricted form of a class. It’s ideal for “plain data carriers,” classes that contain data not meant to be altered and only the most fundamental methods such as constructors and accessors.

Now let us define a controller class that will handle requests related to the users. For simplicity, we will only define one method in this class to register the user. The method will take three parameters – first name, surname, and address and will call the service method. The service is injected into the controller using guice @Inject annotation.

UserController.java

package org.javacodegeeks;

import com.google.inject.Inject;

public class UserController {

    @Inject private UserService userService;

    public void registerUser(String firstname, String surname, String address) {
        userService.registerUser(new User(firstname + "_" + surname, address));
    }
}

Let us now define our simple service interface:

UserService.java

package org.javacodegeeks;

public interface UserService {

    void registerUser(User user);
}

Below is the default implementation of the service:

DefaultUserService.java

package org.javacodegeeks;

public class DefaultUserService implements UserService {

    @Override
    public void registerUser(User user) {
        // TODO - implement
        System.out.println(String.format("User %s registered successfully", user.username()));
    }
}

Now let us bind this default implementation to the service:

BasicModule.java

package org.javacodegeeks;

import com.google.inject.AbstractModule;

public class BasicModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(UserService.class).to(DefaultUserService.class);
    }
}

Now let us create our main class:

Application.java

package org.javacodegeeks;

import com.google.inject.Guice;
import com.google.inject.Injector;

public class Application {

    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new BasicModule());
        UserController userController = injector.getInstance(UserController.class);
        userController.registerUser("Tom", "Cruise", "NYC");
    }
}

Guice is the entry point to the Guice framework. It creates Injectors from Modules. Guice supports a model of development that draws clear boundaries between APIs, Implementations of these APIs, Modules which configure these implementations, and finally Applications that consist of a collection of Modules. It is the Application, which typically defines your main() method, that bootstraps the Guice Injector using the Guice class.

When you run the Application class, you will see an output as below:

User Tom_Cruise registered successfully

4. Conclusion

In this article, we looked at how to use Google Guice for dependency injection. This is a very simple example – there are a lot more advanced features which is out of the scope of this article. For example, you can use Named bindings when you annotate the class which you are binding with the @Named annotation, and in your module, you can do something like the below:

bind(MyInterface.class)
      .annotatedWith(Names.named("NamedBinding"))
      .to(DefaultImpl.class);

We also looked at different ways of binding the classes. In the end, we looked at a very simple working example

5. Download

This was an example of Google Guice.

Download
You can download the full source code of this example here: Google Guice

Mohammad Meraj Zia

Senior Java Developer
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button