Google Guice
View on Wikipedia| Google Guice | |
|---|---|
| Developer | |
| Stable release | 7.0.0
/ May 12, 2023[1] |
| Repository | github |
| Written in | Java |
| Type | Dependency injection framework |
| License | Apache License 2.0 |
| Website | github |
Google Guice (pronounced like "juice")[2] is an open-source software framework for the Java platform developed by Bob Lee and Kevin Bourrillion at Google and released under the Apache License. It provides support for dependency injection using annotations to configure Java objects.[3] Dependency injection is a design pattern whose core principle is to separate behavior from dependency resolution.
Guice allows implementation classes to be bound programmatically to an interface, then injected into constructors, methods or fields using an @Inject annotation. When more than one implementation of the same interface is needed, the user can create custom annotations that identify an implementation, then use that annotation when injecting it.
Being the first generic framework for dependency injection using Java annotations in 2008, Guice won the 18th Jolt Award for best Library, Framework, or Component.[3][4]
See also
[edit]References
[edit]- ^ "Guice510 · google/guice Wiki". GitHub. Retrieved 2022-05-12.
- ^ "google-guice - Guice (pronounced 'juice') is a lightweight dependency injection framework for Java 5 and above, brought to you by Google". Code.google.com. Google Project Hosting. 2007-03-23. Retrieved 2013-11-24.
- ^ a b Yuan, Michael. "Guice (Google)". Retrieved 2010-04-09.
- ^ "18th Annual Jolt Award winners". Dr. Dobb's.
Further reading
[edit]- Vanbrabant, Robbie (April 21, 2008), Google Guice: Agile Lightweight Dependency Injection Framework (1st ed.), Apress, p. 192, ISBN 978-1-59059-997-6
Google Guice
View on Grokipedianew keyword. Its core mechanism revolves around annotations like @Inject for constructor injection, enabling type-safe binding of interfaces to implementations via modules and injectors, which promotes explicit dependencies and avoids hidden global state.[2]
Guice's design principles prioritize simplicity and minimalism, incorporating only features with at least three demonstrated use cases to prevent unnecessary complexity, while providing clear error messages and avoiding "magic" behaviors that could hinder debugging.[1] Key extensions include support for assisted injection, multibindings, and integration with frameworks like Spring, making it suitable for both small-scale and large enterprise Java projects.[1] As of its latest stable release, version 7.0.0 on May 12, 2023, Guice remains actively maintained under the Apache 2.0 license, with ongoing updates for compatibility with modern Java standards such as Jakarta EE.
Overview
Introduction to Dependency Injection
Dependency injection (DI) is a software design pattern that implements inversion of control (IoC) by allowing the dependencies of a class to be provided externally rather than created or located internally by the class itself. This technique promotes loose coupling between software components, enabling high-level modules to interact with abstractions rather than concrete implementations.[3] At its core, DI embodies the inversion of control principle, where the framework or container manages the lifecycle and wiring of objects, inverting the traditional flow of control from application code to the infrastructure. It avoids hard-coded dependencies, such as direct instantiation of collaborators, which reduces rigidity and facilitates substitution of implementations. Additionally, DI enhances testability by permitting the injection of mock or stub objects in place of real dependencies during unit testing.[3] DI can be realized through several mechanisms, including constructor injection, where dependencies are passed via the class constructor to ensure they are available upon instantiation and support immutability; setter injection, which uses setter methods for more flexible, post-construction configuration but risks partial initialization; and field injection, which directly targets instance fields, often via annotations for simplicity though it can complicate testing due to hidden dependencies.[3] The roots of DI trace back to foundational software design concepts, including the Dependency Inversion Principle articulated by Robert C. Martin, which posits that both high-level and low-level modules should depend on abstractions, not concretions, to achieve flexible architectures. This principle builds on earlier ideas like the Hollywood Principle—"Don't call us, we'll call you"—which illustrates how frameworks dictate execution flow rather than libraries being invoked directly. In Java applications, DI yields benefits such as improved modularity, code reusability, and maintainability by decoupling components and easing refactoring. Google Guice serves as an annotation-driven DI framework that applies these concepts in Java environments.[4][5]Guice's Design Philosophy
Guice is a lightweight, annotation-driven dependency injection (DI) framework for Java 11 and above, developed by Google to streamline object wiring and reduce the boilerplate code associated with traditional XML-heavy DI configurations.[1] Unlike earlier frameworks that relied on verbose XML files for defining dependencies, Guice emphasizes declarative annotations such as@Inject directly in Java code, enabling developers to specify dependencies explicitly in constructors or methods while maintaining type safety through generics.[2] This approach separates behavior from dependency resolution, minimizing the need for manual factory classes and promoting modular, testable code.[2]
Central to Guice's philosophy is the balance between flexibility and explicitness, achieved through runtime reflection for just-in-time (JIT) bindings and a preference for avoiding "magic" in configuration. JIT bindings allow the framework to dynamically resolve unbound types at runtime by inspecting injectable constructors or annotations like @ImplementedBy, providing adaptability without requiring exhaustive upfront declarations.[6] However, Guice encourages explicit bindings via modules to prevent hidden dependencies, ensuring transparency and easier debugging, with the option to disable JIT entirely for stricter control.[6] Type safety is further reinforced by leveraging Java generics and annotations, which enable compile-time checks and produce helpful, detailed error messages that pinpoint issues like missing bindings or circular dependencies, contrasting with string-based identifiers in XML configurations that are prone to refactoring errors.[7]
Guice's design also prioritizes standards compliance and extensibility without core bloat, fully implementing JSR-330 for portability across DI containers while extending it with Java-based modules for custom bindings.[8] By focusing solely on core DI mechanisms—such as constructor injection and provider support—rather than encompassing broader application framework features, Guice facilitates faster development cycles through code-over-configuration, reducing manual wiring and enhancing interoperability in Java ecosystems.[7] This philosophy underscores a commitment to developer productivity, where modules serve as lightweight extension points, allowing complex scenarios like custom providers without inflating the framework's footprint.[2]
History
Development and Initial Release
Google Guice originated as an internal tool developed at Google in the mid-2000s to simplify dependency injection in large-scale Java applications, particularly by reducing boilerplate code in services such as AdWords.[2] The framework was created to address the complexities of managing dependencies in multi-million-line codebases, leveraging Java 5's annotations and generics for type-safe, maintainable wiring without heavy reliance on traditional factory patterns. Key creators included Bob Lee, who led the project, along with Jesse Wilson and other Google engineers on the AdWords frontend team, motivated by the need to streamline object assembly and enhance modularity in production systems. Their work focused on a lightweight alternative to existing frameworks, emphasizing constructor injection, basic modules for configuration, and support for fields and methods. Guice 1.0 was publicly released as open-source on March 12, 2007, under the Apache 2.0 license, initially hosted on Google Code before migrating to GitHub at github.com/google/guice.[1] This release introduced annotation-based dependency injection, a novel approach at the time that predated the JSR-330 standard finalized in October 2009.[9] Early adoption was driven by its simplicity and performance, culminating in Guice winning the 2008 Jolt Award for the best library, framework, or component.[10]Major Versions and Updates
Guice 2.0, released on May 19, 2009, introduced provider methods via the @Provides annotation to simplify custom provider creation without boilerplate code, assisted injection using FactoryModuleBuilder for partial parameter injection, and AOP integration compatible with the AOP Alliance library while remaining optional for environments like Android.[11][12] Version 3.0, released on March 24, 2011, achieved full compliance with JSR-330 standards by including the javax.inject library and adopting its annotations, added multibindings through Multibinder and MapBinder classes supporting duplicate permits, and enhanced error reporting with simpler stack traces and more informative exception messages.[13] Guice 4.0, released on April 28, 2015, provided enhanced support for Java 8 features, including the use of lambdas in LinkedBindingBuilder.toProvider for concise provider definitions, and introduced optional injections via OptionalBinder to handle unbound dependencies gracefully.[14] Version 5.0, initially released as a beta in 2020 with a stable update in early 2021, focused on compatibility with Java 11 and later versions, incorporating fixes for reflection access issues under newer Java security models and removing the cglib dependency in favor of direct ASM usage for bytecode generation.[15] Guice 5.1.0, released on January 25, 2022, further improved compatibility with Java 15 and introduced support for the Java Platform Module System (JPMS).[16] The latest stable release, Guice 7.0.0 on May 12, 2023, added full support for Jakarta EE namespaces including jakarta.inject, jakarta.servlet, and jakarta.persistence, while deprecating and removing the javax namespace to align with Jakarta EE migrations; it was released alongside version 6.0.0, which offers dual javax and jakarta support for transitional use.[17] The project is maintained on GitHub, with a total of 19 releases as of May 2023. As of November 2025, it continues to receive commits addressing bug fixes and improvements, though no new version releases have been made since 7.0.0.[18][1]Core Components
The Injector
The Injector serves as the central runtime component in Google Guice, responsible for constructing object graphs by resolving and injecting dependencies based on predefined bindings. It acts as the core engine that assembles the dependency graph at runtime, enabling the framework to manage object lifecycles and interconnections without manual wiring.[19] An Injector is created using the static factory methodGuice.createInjector(modules), where one or more Module instances are passed to configure the bindings. During creation, the Injector processes these modules to build an internal representation of the dependency graph, resulting in an immutable object that ensures consistent behavior across its lifecycle. This immutability allows the Injector to be safely shared and used for thread-safe instance retrieval in multi-threaded environments, typically instantiated once during application startup.[20][19]
Key methods of the Injector facilitate explicit dependency management. The getInstance(Class<T> type) method retrieves an instance of the specified type by resolving its dependencies and constructing it if necessary, equivalent to invoking getProvider(type).get(). For lazy access, getProvider(Key<T> key) returns a Provider<T> that supplies instances on demand, useful for avoiding premature initialization. Additionally, injectMembers(Object instance) populates fields and methods marked for injection on an existing instance, bypassing constructor injection to support legacy or partially wired objects. These methods throw ProvisionException or ConfigurationException if resolution fails.[19]
Internally, the Injector employs Java reflection to inspect types, resolve their dependencies recursively, and instantiate objects according to the graph. For types without explicit bindings, it supports just-in-time (JIT) binding creation, generating implicit bindings on the first request and caching them for subsequent uses to optimize performance. Instance caching aligns with configured scopes, reusing objects where appropriate to manage memory and consistency. This reflective approach leverages Java's type system, including generics, to ensure type-safe injections while minimizing runtime overhead.[19][21]
The Injector includes robust error handling to aid debugging. It detects and reports circular dependencies during graph construction or resolution, providing detailed stack traces and paths involved. For missing bindings, it issues a ConfigurationException with specifics on the unresolved key and potential causes, such as absent module configurations, facilitating quick identification and correction of issues.[19][21]
Modules and Bindings
In Google Guice, modules serve as the central mechanism for configuring dependency bindings, enabling developers to define how interfaces and classes are resolved during injection. A module is typically implemented by extending theAbstractModule class and overriding its configure() method, where bindings are specified using the Binder interface provided by Guice. This approach promotes modularity, as multiple modules can be composed and passed to Guice.createInjector(modules) to assemble the complete dependency graph for an application.[20]
Bindings within a module's configure() method link injection keys—comprising a type and optional annotations—to specific targets, allowing Guice to instantiate and provide dependencies at runtime. Common binding types include linked bindings, which map an interface or abstract class to a concrete implementation via bind(Interface.class).to(Implementation.class); instance bindings, which supply a pre-existing object using bind(Type.class).toInstance(value) for constants like configuration strings; and provider bindings, which delegate to a Provider<T> implementation for custom instantiation logic with bind(Type.class).toProvider(ProviderClass.class). These bindings ensure type-safe configuration, with compile-time checks available through the Guice Berry extension for enhanced error detection.[22]
For scenarios requiring qualified or disambiguated dependencies, Guice supports annotated bindings using built-in annotations like @Named from com.google.inject.name or custom qualifiers. For instance, bind(String.class).annotatedWith(Names.named("databaseUrl")).toInstance("jdbc:[mysql](/page/MySQL)://[localhost](/page/Localhost)") distinguishes between multiple string bindings, preventing ambiguity in injection points. Custom qualifiers extend @Qualifier and follow the same pattern, enabling fine-grained control over dependency resolution without altering class hierarchies.[20]
Guice also facilitates multibindings for one-to-many relationships, collecting multiple implementations into sets or maps. Set multibindings use Multibinder<T>, created via Multibinder.newSetBinder(binder(), ElementType.class), with additions like multibinder.addBinding().toInstance("value") or multibinder.addBinding().to(Implementation.class); the resulting set is injectable as Set<T>. Map multibindings employ MapBinder<KeyType, ValueType> from MapBinder.newMapBinder(binder(), KeyType.class, ValueType.class), binding entries with mapBinder.addBinding(key).toInstance(value), yielding an injectable Map<KeyType, ValueType>. These constructs are particularly useful for extensible plugins or configuration aggregators.
Just-in-time (JIT) bindings provide an automatic fallback mechanism when no explicit binding exists for a requested type, allowing Guice to instantiate classes dynamically without full module configuration upfront. For concrete classes, Guice searches for an injectable constructor—typically a no-argument, non-private one in a non-private class—or one annotated with @Inject; if multiple constructors qualify, the one with the fewest parameters is chosen. Interfaces can leverage the @ImplementedBy(Implementation.class) annotation to specify a default concrete type, while @ProvidedBy(ProviderClass.class) enables custom providers for more complex cases. To enforce explicit bindings and disable JIT for stricter control, developers can call binder().requireExplicitBindings() in modules, which throws exceptions for unresolved dependencies at injector creation time.[6]
Best practices for modules and bindings emphasize maintainability and testability: keep individual modules focused on a single concern or package to facilitate reuse and comprehension, avoiding monolithic configurations. For testing, use Modules.override(existingModules).with(testModule) to selectively replace production bindings with mocks or stubs without altering source modules. Circular bindings, which can arise from mutual dependencies, should be avoided by designing acyclic graphs; Guice detects and reports them as errors during injector construction. Additionally, prefer interfaces for bindings to support polymorphism, and limit instance bindings to immutable constants to prevent shared mutable state issues.[20][23][6]
Annotations and Injection
Key Annotations
Google Guice relies on a set of annotations to declare injection points and configure bindings, enabling declarative dependency management without boilerplate code. These annotations, many of which align with the JSR-330 standard, allow developers to mark constructors, fields, methods, and classes for automatic dependency resolution by the Injector. By using these annotations, Guice promotes loose coupling and testability in Java applications.[8] The core annotation for dependency injection in Guice is@Inject, which identifies points where dependencies should be provided by the Injector. It can be applied to constructors for constructor injection, fields for field injection, or methods for method injection, with Guice preferring constructor injection when available. For optional dependencies in field or method injection, @Inject includes an optional element set to true, which prevents errors if no binding exists, though this is distinct from the @Nullable annotation (from javax.annotation.Nullable) that marks individual parameters or fields as potentially null. In practice, a constructor annotated with @Inject might look like public MyClass(@Inject Dependency dep) {}, allowing Guice to resolve and inject Dependency at runtime. Note that optional=true on constructors does not prevent errors for missing bindings, as Guice will fail during Injector creation.[24]
To disambiguate multiple bindings for the same type, Guice provides @Named and custom @Qualifier annotations, which qualify dependencies with additional metadata. The @Named annotation, part of both Guice and JSR-330, uses a string value to specify a binding name, such as @Named("email") String communicatorType, enabling selection among bindings like email or SMS communicators without type changes. Custom qualifiers are defined by creating annotations meta-annotated with @Qualifier (or the older @BindingAnnotation), targeting fields, parameters, and methods with runtime retention; for example, @interface Fast {} annotated with @Qualifier allows bindings like bind(new AnnotatedBindingBuilderImpl<Processor>(binder(), Key.get(Processor.class, Fast.class))). These qualifiers form a Key combining type and annotation, ensuring precise matching during injection.[25][8]
In modules, the @Provides annotation facilitates method-based bindings for complex object creation, where the method's return type becomes the bound type and its parameters are injected dependencies. Placed on methods within a Module implementation's configure()-overridden methods or as static methods, @Provides allows logic like resource initialization; for instance, @Provides Database provideDatabase(@Named("host") [String](/page/String) host) { return new Database(host); } binds Database to the method's result. This approach is particularly useful for non-trivial factories or third-party integrations, introduced in Guice 2.0 for enhanced flexibility.[26]
Guice also supports annotations for default implementations and just-in-time (JIT) bindings. The @ImplementedBy annotation, applied to interfaces or abstract classes, specifies a default concrete implementation class, such as @ImplementedBy(DefaultPaymentProcessor.class) on PaymentProcessor, enabling automatic binding without explicit module configuration. For JIT bindings, Guice automatically creates providers for concrete classes with injectable constructors (those annotated with @Inject or parameterless), but @ImplementedBy overrides this for interfaces; note that explicit bind() statements take precedence over the annotation. While no dedicated @Injectable annotation exists, classes are deemed injectable if they meet constructor criteria, supporting seamless JIT resolution.[27][6]
Guice achieves full compliance with JSR-330 (Dependency Injection for Java) since version 3.0, standardizing annotations like @Inject, @Named, @Qualifier, @Scope, and @Singleton from the javax.inject (or jakarta.inject in later versions) package. This interoperability allows Guice code to work with other JSR-330-compliant frameworks, though Guice retains its proprietary annotations (e.g., @Provides, @ImplementedBy) for extended functionality. Developers can mix standard and Guice-specific annotations, with JSR-330 ones ensuring portability across injectors.[8]
Injection Mechanisms
Google Guice employs several mechanisms to resolve and inject dependencies at runtime, primarily triggered during object instantiation via theInjector.getInstance() method or through manual invocation using Injector.injectMembers(). These processes leverage the @Inject annotation to identify injection points, allowing Guice to traverse bindings defined in modules and construct an internal dependency graph for resolution. The framework prioritizes constructor injection for its immutability and testability benefits, while supporting alternatives for specific scenarios.[28]
Constructor injection is the preferred mechanism, where the @Inject annotation on a class constructor signals Guice to provide all required dependencies as parameters during object creation. This ensures dependencies are fully satisfied before the object is usable, promoting immutable designs with final fields. For instance, in a class RealBillingService, the constructor might be annotated as follows:
public class RealBillingService {
private final CreditCardProcessor processor;
private final TransactionLog database;
@Inject
public RealBillingService(CreditCardProcessor processor, TransactionLog database) {
this.processor = processor;
this.database = database;
}
}
Guice resolves the parameters by matching them to bindings and invokes the constructor only after all dependencies are available, throwing a ProvisionException if resolution fails.[28]
Field injection and method injection serve as alternatives for scenarios like legacy code or setter-based wiring, where dependencies are injected directly into fields or method parameters marked with @Inject. Field injection assigns values post-construction, implying mutability and reduced testability since fields are not final. Method injection, often used for optional or dynamic dependencies, requires manual invocation via injectMembers() on an existing instance. For example:
public class PayPalService {
@Inject private String apiKey;
@Inject
public void setTransactionLog(TransactionLog log) {
this.log = log;
}
}
To apply these, one would call injector.injectMembers(new PayPalService()), which resolves and injects the dependencies without recreating the object. These methods are less favored than constructor injection due to potential partial initialization states.[28]
Provider injection defers dependency resolution by injecting an instance of Provider<T>, enabling lazy or conditional access to dependencies via the get() method. This is particularly useful in static contexts or when the dependency's lifecycle needs explicit control. Guice binds Provider<T> automatically for any type T, resolving the underlying dependency only upon invocation. An example in a static field:
public class StaticProcessor {
@Inject static Provider<Processor> processorProvider;
public static void process() {
processorProvider.get().run();
}
}
This mechanism avoids eager resolution, reducing overhead for rarely used dependencies, though unscoped providers may create new instances each time get() is called.[28]
Guice handles circular dependencies—where two or more classes mutually depend on each other—through strategies like injecting Provider<T> to break the cycle by delaying resolution, or by generating runtime proxies for interface-based dependencies. For instance, if class A depends on B and vice versa, injecting Provider<B> into A allows A to be constructed first, with B obtained later via get(). Alternatively, Guice's circular proxy feature creates a proxy for one dependency during construction, injecting the fully resolved instance post-cycle. This proxy support can be disabled if not needed, and for tighter couplings, assisted injection via factory methods provides parameterized creation without full proxying. These approaches prevent infinite recursion during resolution.[29]
For optional or nullable injections, Guice supports graceful handling of absent bindings using @Inject(optional=true) on fields or methods, which silently skips injection if the dependency is unbound. This is ideal for non-essential services. For null values, the @Nullable annotation on injection points permits null injection, overriding Guice's default rejection of nulls with a ProvisionException. An example combining both:
public class FlexibleService {
@Inject(optional = true)
@Nullable
private DatabaseConnection connection;
}
If no binding exists, the field remains null without error; otherwise, a bound null provider would also succeed due to @Nullable. Prefer Optional<T> bindings for modern Java to encapsulate absence explicitly.[30][28]
Advanced Features
Scopes and Lifecycles
Guice manages the lifecycle of injected objects through scopes, which determine the visibility and reuse of instances provided by the injector. By default, bindings have no scope, resulting in a new instance created each time the dependency is injected. This unscoped behavior ensures fresh objects for stateless or short-lived components but can lead to inefficient recreation of expensive resources.[31] Guice includes a built-in@Singleton scope, which reuses a single instance for the lifetime of the injector, promoting efficiency for shared, thread-safe objects across the application. The @Singleton annotation can be applied directly to implementation classes or @Provides methods, and it supports both javax.inject.Singleton and Guice's own variant. For eager initialization of singletons—useful in production to detect configuration errors early—bindings can use asEagerSingleton(), which provisions the instance during injector creation in the PRODUCTION stage but lazily in DEVELOPMENT.[31][32][33]
Custom scopes extend Guice's flexibility by implementing the Scope interface, allowing developers to define behaviors like per-request or per-session lifecycles, particularly in web applications. A custom scope requires a scoping annotation marked with @ScopeAnnotation, followed by binding the annotation to a Scope implementation in a module via bindScope(). Implementations often extend helper classes like SimpleScope for thread-local storage, using enter() to activate the scope and exit() to deactivate it, ensuring instances are scoped to specific contexts such as HTTP requests. The injector tracks these scoped instances internally, and custom scopes can include cleanup logic, such as a clear() method to release resources at the end of the scope's lifecycle.[34][35][31]
Scopes are applied declaratively in modules, overriding any class-level annotations; for example, bind(Foo.class).to(FooImpl.class).in(Scopes.SINGLETON) ensures singleton reuse while integrating with providers for complex initialization. Guice supports post-construction setup through staged injection, where @Inject-annotated methods are invoked after object creation and field injection, enabling initialization logic without altering constructors. In multi-threaded environments, scoping heavy objects like database connections or caches optimizes performance by minimizing instantiation overhead, though scoped instances must be designed for concurrency where applicable, such as thread-safety for singletons.[31][31]
Aspect-Oriented Programming Support
Google Guice provides support for aspect-oriented programming (AOP) through method interception, allowing developers to add cross-cutting concerns such as logging, security, or transaction management without modifying the core business logic of classes.[36] This feature enables the execution of custom code before, after, or around the invocation of specific methods in injected objects.[36] Guice is compliant with the AOP Alliance specifications, utilizing theMethodInterceptor interface to implement around-advice for method interception.[37] Interceptors are bound within Guice modules using the bindInterceptor method, which requires two matchers: one for selecting target classes and another for selecting methods within those classes.[36] For example, a module might configure an interceptor as follows:
bindInterceptor(Matchers.any(),
Matchers.annotatedWith(Log.class),
new LoggingInterceptor());
This binds a LoggingInterceptor to any method annotated with @Log across all classes.[36][38]
Guice employs type-safe matchers from the com.google.inject.matcher.Matchers class to precisely target classes and methods for interception.[38] Common matchers include Matchers.any() for all classes or methods, Matchers.annotatedWith(Class) for elements bearing a specific annotation (e.g., @Transactional for transaction management), and Matchers.only(Class) to restrict interception to a single class.[36][38] Additional matchers like subclassesOf(Class) or inPackage(Package) allow for more granular selection, such as intercepting all methods in subclasses of a service interface or within a specific package.[38]
Typical use cases for Guice AOP include implementing transaction management by intercepting annotated methods to begin and commit transactions, adding caching layers to memoize expensive operations, and performing validation or authorization checks before method execution.[36] For instance, a security interceptor could enforce access controls on methods annotated with @Secure, integrating seamlessly with Guice scopes to apply aspects only to scoped instances like singletons or request-scoped objects.[36] Logging is a common example, where an interceptor wraps method calls to record entry and exit points with timestamps.[37]
Guice's AOP support operates at runtime through bytecode generation and does not involve compile-time weaving, distinguishing it from tools like AspectJ.[36] Limitations include the requirement that intercepted classes and methods must be non-final and have appropriate visibility (public, package-private, or protected), and instances must be created by Guice via @Inject constructors or no-arg constructors.[36] Additionally, this feature relies on bytecode manipulation and is not supported on platforms like Android without optional extensions.[36]
Usage Examples
Basic Dependency Injection
Basic dependency injection in Google Guice involves defining interfaces for services, implementing them, configuring bindings in a module, and using an injector to resolve and provide instances automatically via constructor injection.[2] This approach decouples the client code from specific implementations, allowing for easier testing and maintenance.[2] Consider a simple billing system example where aBillingService depends on a CreditCardProcessor to handle payments and a TransactionLog to record outcomes. First, define the interfaces:
public interface CreditCardProcessor {
ChargeResult charge(CreditCard creditCard, BigDecimal amount);
}
public interface TransactionLog {
void logConnected(ConnectResponse connectResponse);
void logChargeResult(ChargeResult result);
void logConnectException(UnreachableException e);
}
public interface BillingService {
Receipt chargeOrder(PizzaOrder order, CreditCard creditCard);
}
Next, provide concrete implementations, such as PaypalCreditCardProcessor for CreditCardProcessor and DatabaseTransactionLog for TransactionLog, with RealBillingService as the implementation for BillingService. The RealBillingService uses constructor injection to receive its dependencies:
public class RealBillingService implements BillingService {
private final CreditCardProcessor processor;
private final TransactionLog transactionLog;
@Inject
public RealBillingService(CreditCardProcessor processor,
TransactionLog transactionLog) {
this.processor = processor;
this.transactionLog = transactionLog;
}
public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
// Implementation details: charge via processor and log result
// ...
return Receipt.forSuccessfulCharge(order.total());
}
}
Configure the bindings in a module that extends AbstractModule:
public class BillingModule extends AbstractModule {
@Override
protected void configure() {
bind(BillingService.class).to(RealBillingService.class);
bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
bind(TransactionLog.class).to(DatabaseTransactionLog.class);
}
}
Create the injector and retrieve the service instance:
public class BillingClient {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new BillingModule());
BillingService billingService = injector.getInstance(BillingService.class);
PizzaOrder order = new PizzaOrder(3);
CreditCard card = new CreditCard("1234-5678", "John Smith");
Receipt receipt = billingService.chargeOrder(order, card);
// receipt confirms successful charge without manual dependency instantiation
}
}
When executed, this code resolves the dependency graph automatically: Guice instantiates RealBillingService with a PaypalCreditCardProcessor and DatabaseTransactionLog, processes the order, and produces a receipt, all without explicit new calls in the client code.[2]
A common pitfall is omitting a binding in the module, which results in a CreationException at runtime when the injector attempts to fulfill an unbound dependency; this is resolved by adding explicit bindings for all required types in the module configuration.[2]
Advanced Module Configuration
Advanced module configuration in Google Guice allows developers to handle more sophisticated dependency injection scenarios beyond simple bindings, enabling dynamic object creation, disambiguation of multiple implementations, and extensible architectures. This includes the use of provider methods for instantiating complex objects, qualifiers to differentiate bindings, multibindings for collecting multiple implementations, and mechanisms for overriding configurations in specific environments like testing. These features promote modular, testable code by separating configuration logic into modules while supporting real-world requirements such as plugin systems and environment-specific swaps.[39][25][40] To disambiguate multiple implementations of the same interface, Guice provides the@Named annotation as a built-in qualifier, which uses string identifiers to specify bindings. For instance, in a payment processing system, different processors can be bound with unique names: bind(PaymentProcessor.class).annotatedWith(Names.named("credit")).to(CreditCardProcessor.class); and bind(PaymentProcessor.class).annotatedWith(Names.named("[paypal](/page/PayPal)")).to(PayPalProcessor.class);. Injection then targets the specific variant, such as @Inject public BillingService(@Named("credit") PaymentProcessor processor) {...}, ensuring the correct implementation is used without type ambiguity. This approach, while convenient, is recommended sparingly due to its reliance on string literals rather than type-safe custom annotations.[25]
Provider methods, annotated with @Provides in a module, enable dynamic instance creation when bindings require custom logic, such as configuring resources like database connections. These methods can depend on other injected parameters and return fully initialized objects. For example, a module might first bind constants in configure():
bindConstant().annotatedWith(Names.named("db.url")).to("jdbc:mysql://localhost/db");
bindConstant().annotatedWith(Names.named("db.username")).to("user");
Then define:
@Provides
[DataSource](/page/Datasource) provideDataSource(@Named("db.url") [String](/page/String) url, @Named("db.username") [String](/page/String) username) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(username);
return new HikariDataSource(config);
}
This binds [DataSource](/page/Datasource) to a configured connection pool, invoked lazily by the injector only when needed, supporting scenarios where direct constructor binding is insufficient.[39]
Multibindings facilitate plugin architectures by collecting multiple implementations into injectable collections like Set<T> or Map<K, V>. Using Multibinder.newSetBinder(binder(), Plugin.class) allows modules to contribute implementations, such as addBinding().to(EmailPlugin.class), resulting in a Set<Plugin> injectable into consumers for iteration over all plugins. Similarly, MapBinder associates keys with values, e.g., addBinding("email").to(EmailPlugin.class), yielding a [Map<String, Plugin>](/page/Map) for keyed access. This is particularly useful for extensible systems, where third-party modules can add plugins without modifying core code, and duplicates can be permitted via .permitDuplicates(). Provider methods can also contribute using @ProvidesIntoSet or @ProvidesIntoMap.[40]
For environment-specific configurations, such as testing, Guice supports overriding bindings via Modules.override(modules).with(overrideModule), which prioritizes the override for conflicting keys. This can swap production implementations with mocks, e.g., Injector injector = Guice.createInjector(Modules.override(new ProductionModule()).with(new TestModule()));. Additionally, the Stage enum differentiates behaviors: Stage.DEVELOPMENT (default) optimizes for fast startup with lazy singletons, while Stage.PRODUCTION enables eager initialization for early error detection, passed to createInjector(Stage.PRODUCTION, modules). In tests, Stage.DEVELOPMENT is often used to avoid production overhead while applying overrides.[41]
The following example integrates these concepts in a web service module, using qualifiers for processors, a provider for database setup, multibindings for notification plugins, and singleton scoping for a repository:
public class WebServiceModule extends AbstractModule {
@Override
protected void configure() {
// Qualifiers for multiple processors
bind(PaymentProcessor.class).annotatedWith(Names.named("[credit](/page/Credit)")).to(CreditCardProcessor.class);
bind(PaymentProcessor.class).annotatedWith(Names.named("[paypal](/page/PayPal)")).to(PayPalProcessor.class);
// Multibindings for plugins
Multibinder<NotificationPlugin> plugins = Multibinder.newSetBinder(binder(), NotificationPlugin.class);
plugins.addBinding().to(EmailPlugin.class);
plugins.addBinding().to(SmsPlugin.class);
// Singleton repository
bind(UserRepository.class).to(DatabaseUserRepository.class).in(Singleton.class);
}
@Provides
@Singleton
[DataSource](/page/Datasource) provideDataSource() {
// Dynamic DB connection setup
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:[mysql](/page/MySQL)://localhost/webservice");
config.setUsername("user");
config.setPassword("pass");
return new HikariDataSource(config);
}
}
In a test, this can be overridden: Modules.override(new WebServiceModule()).with(new MockPaymentModule()), creating an injector with mocked processors while retaining other bindings. This configuration supports a scalable web service where the OrderService injects @Named("[credit](/page/Credit)") PaymentProcessor, Set<NotificationPlugin>, and the singleton UserRepository backed by the provided [DataSource](/page/Datasource).[39][25][40][41]