← Back to list

Dependency injection using Google Guice with examples

What is dependency?

Rakesh raj · 2024-02-11 05:27 · 1 claps · 3.6 min read
#guice #java #google #dependency-injection #inversion-of-control
Open on Medium ↗

Dependency injection using Google Guice with examples

What is dependency?

I will explain it through an example. In a team where you are the sole member responsible for handling backend tasks, your team depends on you. In this context, you represent the dependency of your team. The company’s goal is to ensure that they are not reliant on any single individual. Instead, they aim to have the flexibility to hire new team members seamlessly, regardless of specific individuals, thus reducing dependency on any one person. This is where dependency injection comes in.

Service-based company example:

Dependency injection (DI) is like getting your new employees from a third-party service instead of hiring them yourself.

Inversion of Control (IoC) is like the company deciding who does what tasks, rather than each employee deciding for themselves. This way, the company stays in control, ensuring tasks get done regardless of who’s on the team.

Different types of dependency injection:

Constructor injection: In constructor injection, we can inject the dependency using the constructor.

class  StudentScore{
    private String schoolName;
    // Here we don't know the map implementaion 
    // so it is dependent on my class
    Map<Integer, Integer> idToScoreMap;
    public StudentScore(Map<Integer, Integer> map) {
        this.idToScoreMap = map;
    }
}

Method Injection:

class SchoolScore {
    private String schoolName;
    Map<Integer, Integer> idToScoreMap;
    public void setIdToScoreMap(Map<Integer, Integer> idToScoreMap) {
        this.idToScoreMap = idToScoreMap
    }
}

Advantages of Dependency Injection:

  1. Decoupling:- DI promotes loose coupling between components since dependencies are provided externally, making classes easier to maintain and test.
  2. Testability: — With DI, dependencies can be easily substituted with mock or stub implementations during testing, allowing for more effective unit testing.
  3. Reusability: — Classes become more reusable as they do not directly create their dependencies, making it easier to use them in different contexts.
dependency-injection-example
  ├── src
  │   └── main
  │       └── java
  │           ├── com
  │           │   └── example
  │           │       ├── UserRepository.java
  │           │       └── UserService.java
  │           └── App.java
  └── pom.xml
package com.example;

public class UserRepository {
    public void saveUser(String username) {
        System.out.println("Saving user: " + username);
    }
}
package com.example;

public class UserService {
    private UserRepository userRepository;

    // Constructor Injection
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void createUser(String username) {
        userRepository.saveUser(username);
    }
}
package com.example;

public class App {
    public static void main(String[] args) {
        // Creating UserRepository instance
        UserRepository userRepository = new UserRepository();
        UserService userService = new UserService(userRepository);
        userService.createUser("JohnDoe");
    }
}

Dependency Injection Containers (DI Containers):

DI Containers are frameworks or libraries that automate the process of dependency injection. Examples include Spring Framework (for Java), Google Guice, Microsoft Unity (for . NET), etc.

In this blog, we will use Google Guice as a dependency container.

Let’s learn some basic terms that will be used very frequently in Google Guice.

Bindings: Bindings define how dependencies are provided. Guice uses modules to declare bindings.

Modules: Modules are classes that configure the bindings between interfaces and their implementations. They are used to wire the dependencies together.

Injector: The injector is responsible for creating instances of classes and injecting their dependencies according to the bindings specified in the modules.

Dependency Injection: Identify the dependencies of your classes and think about how they can be provided from outside rather than created internally.

Setting Up Guice:

  1. Add Guice as a dependency to your project.

  2. Create a Guice module where you configure your bindings.

  3. Create an injector to initialize your application.

Modules:

  1. Add Guice as a dependency to your project.

  2. Create a Guice module where you configure your bindings.

  3. Create an injector to initialize your application.

<dependencies>
    <dependency>
        <groupId>com.google.inject</groupId>
        <artifactId>guice</artifactId>
        <version>5.0.1</version>
    </dependency>
</dependencies>
public class AppModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(UserRepository.class);
        bind(UserService.class);
    }
}
public class UserRepository {
    public void saveUser(String username) {
        System.out.println("Saving user: " + username);
    }
}
public class UserService {
    private UserRepository userRepository;

    @Inject
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void createUser(String username) {
        userRepository.saveUser(username);
    }
}
public class App {
    public static void main(String[] args) {
        // Create Guice injector with AppModule
        Injector injector = Guice.createInjector(new AppModule());

        // Get UserService instance from injector
        UserService userService = injector.getInstance(UserService.class);

        userService.createUser("JohnDoe");
    }
}

So either you can directly create an injector container in the Main method but in this case, you can’t access the injector from other class files, so it’s better to separate this in another class.

public class AppGuiceInjector {
    private static Injector injector;

    private AppGuiceInjector() {
    }

    public static synchronized Injector getInjector() {
        if (injector == null) {
            injector = Guice.createInjector(new AppModule());
        }

        return injector;
    }

    @VisibleForTesting
    public static synchronized void setInjector(Injector aInjector) {
        injector = aInjector;
    }
}

Whenever Guice creates an instance, it performs this injection automatically (after first performing the constructor injection), so if you’re able to let Guice create all your objects for you, you’ll never need to use InjectMembers(this).

InjectMembers(this) :

public class MyApp {
    @Inject MyService myService;

    public MyApp() {
        // Create an instance of MyApp
        MyApp app = new MyApp();

        // Create a Guice injector
        Injector injector = Guice.createInjector(new MyModule());

        // Inject dependencies into the instance of MyApp
        injector.injectMembers(app);

        // Now 'myService' field in 'app' is injected with the appropriate dependency
    }
}

In the example above, MyModule is a Guice module that provides bindings for the dependencies that MyApp needs. The injectMembers() call injects those dependencies into the instance of MyApp.

Best Practices:

Keep Modules Small: Modules should be focused and contain bindings related to a specific concern

Prefer Constructor Injection: Constructor injection is the preferred way of injecting dependencies as it makes dependencies explicit and helps in testing.

Avoid Using Guice in Domain Classes: Domain classes should not be aware of Guice. Keep Guice annotations and logic confined to your configuration classes.


메타데이터
post_id
e59f28ec1844
slug
dependency-injection-using-google-guice-with-examples-e59f28ec1844
url
https://medium.com/@rrlinus5/dependency-injection-using-google-guice-with-examples-e59f28ec1844
canonical_url
https://medium.com/@rrlinus5/dependency-injection-using-google-guice-with-examples-e59f28ec1844
author_url
https://medium.com/@rrlinus5
status
ok
fetched_at
2026-06-28 04:42:08