Exploring Dependency Injection Patterns in Guice — Provider, AssistedInject, and Factory Modules
Introduction
Exploring Dependency Injection Patterns in Guice — Provider, AssistedInject, and Factory Modules

Image Source
Introduction
Dependency Injection is a design pattern that allows us to decouple the creation of objects from their usage. It helps us create more maintainable, testable, and modular code. In this article we will explore three dependency injection patterns in Guice: Provider, AssistedInject, and Factory Modules. We will also provide code examples to demonstrate the usage of each pattern.
Provider Pattern
The Provider pattern is a straightforward way to inject dependencies in Guice. A provider is a simple Java class that implements the Provider<T> interface, where T is the type of object it will provide. Providers are responsible for creating instances of the object they provide.
Code Example:
public class EngineProvider implements Provider<Engine> {
@Override
public Engine get() {
return new V8Engine();
}
}
public class Car {
private final Engine engine;
@Inject
public Car(Engine engine) {
this.engine = engine;
}
}
public class CarModule extends AbstractModule {
@Override
protected void configure() {
bind(Engine.class).toProvider(EngineProvider.class);
}
}
In this example, we have an EngineProvider class that provides an instance of a V8Engine. The Car class has a dependency on the Engine interface, and the CarModule binds the Engine to the EngineProvider.
AssistedInject Pattern
The AssistedInject pattern allows you to mix automatically injected dependencies with manually provided arguments in a constructor. This pattern is useful when some dependencies cannot be resolved by the injector at runtime.
Code Example:
public class Car {
private final Engine engine;
private final String color;
@AssistedInject
public Car(Engine engine, @Assisted String color) {
this.engine = engine;
this.color = color;
}
}
public interface CarFactory {
Car create(String color);
}
public class CarModule extends AbstractModule {
@Override
protected void configure() {
install(new FactoryModuleBuilder().implement(Car.class, Car.class).build(CarFactory.class));
}
}
In this example, the Car class has an additional color field. We use the @AssistedInject annotation to indicate that the color parameter should be provided manually, while the Engine dependency should be injected automatically. We also define a CarFactory interface that allows us to create instances of Car with the specified color.
Factory Modules
Factory Modules provide a more flexible way to create instances of objects with complex dependencies or configuration. They use the AbstractFactory pattern, where an interface defines the factory methods for creating instances.
Code Example:
public interface EngineFactory {
Engine create(String type);
}
public class EngineFactoryImpl implements EngineFactory {
@Inject
public EngineFactoryImpl() {}
@Override
public Engine create(String type) {
switch (type) {
case "V8":
return new V8Engine();
case "V6":
return new V6Engine();
default:
throw new IllegalArgumentException("Unknown engine type: " + type);
}
}
}
public class CarModule extends AbstractModule {
@Override
protected void configure() {
bind(EngineFactory.class).to(EngineFactoryImpl.class);
}
}
In this example, we have an EngineFactory interface and an implementation EngineFactoryImpl. The factory allows us to create different types of engines based on the provided type parameter. The CarModule binds the EngineFactory to the EngineFactoryImpl implementation.
With the Factory Modules pattern, you can create instances of objects that have complex dependencies or require dynamic configuration based on input parameters. This pattern is particularly useful when you have multiple implementations of an interface and you need to decide which one to use at runtime.
Code Example:
public class Car {
private final Engine engine;
@Inject
public Car(EngineFactory engineFactory, @Named("engineType") String engineType) {
this.engine = engineFactory.create(engineType);
}
}
public class CarModule extends AbstractModule {
@Override
protected void configure() {
bind(String.class).annotatedWith(Names.named("engineType")).toInstance("V8");
bind(EngineFactory.class).to(EngineFactoryImpl.class);
}
}
In this example, the Car class has a dependency on the EngineFactory interface. It uses the factory to create an instance of the Engine based on the engineType parameter, which is injected using the @Named annotation. The CarModule binds the engineType parameter to the "V8" value and binds the EngineFactory to the EngineFactoryImpl implementation.
Conclusion
We have explored three dependency injection patterns in Guice: Provider, AssistedInject, and Factory Modules. Each pattern has its own advantages and is suitable for different scenarios:
- Provider Pattern: A simple way to inject dependencies, suitable for most use cases.
- AssistedInject Pattern: Allows mixing automatically injected dependencies with manually provided arguments, useful for constructors that require both.
- Factory Modules: Provides a flexible way to create instances of objects with complex dependencies or configuration, suitable for cases where you need to decide which implementation to use at runtime.
By understanding and leveraging these patterns, you can create more maintainable, testable, and modular code with Guice.
메타데이터
- post_id
- fca6cdc4ab7e
- slug
- exploring-dependency-injection-patterns-in-guice-provider-assistedinject-and-factory-modules-fca6cdc4ab7e
- url
- https://medium.com/@AlexanderObregon/exploring-dependency-injection-patterns-in-guice-provider-assistedinject-and-factory-modules-fca6cdc4ab7e
- canonical_url
- https://medium.com/@AlexanderObregon/exploring-dependency-injection-patterns-in-guice-provider-assistedinject-and-factory-modules-fca6cdc4ab7e
- author_url
- https://medium.com/@AlexanderObregon
- status
- ok
- fetched_at
- 2026-06-29 01:02:39