-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Scopes
By default, Guice returns a new instance each time it supplies a value. This behaviour is configurable via scopes. Scopes allow you to reuse instances: for the lifetime of an application, a session, or a request.
Guice comes with a built-in @Singleton
scope that reuses the same instance
during the lifetime of an application within a single injector. Both
javax.inject.Singleton
and com.google.inject.Singleton
are supported by
Guice, but prefer the standard javax.inject.Singleton
since it is also
supported by other injection frameworks like Dagger.
If using
Stage.PRODUCTION
, Guice will provision all singleton scoped bindings during injector
creation. Exceptions thrown while provisioning singleton scoped bindings may prevent
the injector from being created.
The servlet extension includes
additional scopes for web apps such as @RequestScoped
.
Guice uses annotations to identify scopes. Specify the scope for a type by
applying the scope annotation to the implementation class. As well as being
functional, this annotation also serves as documentation. For example,
@Singleton
indicates that the class is intended to be threadsafe.
@Singleton
public class InMemoryTransactionLog implements TransactionLog {
/* everything here should be threadsafe! */
}
Scopes can also be configured in bind
statements:
bind(TransactionLog.class).to(InMemoryTransactionLog.class).in(Singleton.class);
And by annotating @Provides
methods:
@Provides @Singleton
TransactionLog provideTransactionLog() {
...
}
If there's conflicting scopes on a type and in a bind()
statement, the
bind()
statement's scope will be used. If a type is annotated with a scope
that you don't want, bind it to Scopes.NO_SCOPE
.
In linked bindings, scopes apply to the binding source, not the binding target.
Suppose we have a class Applebees
that implements both Bar
and Grill
interfaces. These bindings allow for two instances of that type, one for
Bar
s and another for Grill
s:
bind(Bar.class).to(Applebees.class).in(Singleton.class);
bind(Grill.class).to(Applebees.class).in(Singleton.class);
This is because the scopes apply to the bound type (Bar
, Grill
), not the
type that satisfies that binding (Applebees
). To allow only a single instance
to be created, use a @Singleton
annotation on the declaration for that class.
Or add another binding:
bind(Applebees.class).in(Singleton.class);
This binding makes the other two .in(Singleton.class)
clauses above
unnecessary.
The in()
clause accepts either a scoping annotation like RequestScoped.class
and also Scope
instances like ServletScopes.REQUEST
:
bind(UserPreferences.class)
.toProvider(UserPreferencesProvider.class)
.in(ServletScopes.REQUEST);
The annotation is preferred because it allows the module to be reused in
different types of applications. For example, an @RequestScoped
object could
be scoped to the HTTP request in a web app and to the RPC when it's in an API
server.
Guice has special syntax to define singletons that can be constructed eagerly:
bind(TransactionLog.class).to(InMemoryTransactionLog.class).asEagerSingleton();
Eager singletons reveal initialization problems sooner, and ensure end-users get
a consistent, snappy experience. Lazy singletons enable a faster
edit-compile-run development cycle. Use the Stage
enum to specify which
strategy should be used.
PRODUCTION | DEVELOPMENT | |
---|---|---|
.asEagerSingleton() | eager | eager |
.in(Singleton.class) | eager | lazy |
.in(Scopes.SINGLETON) | eager | lazy |
@Singleton | eager* | lazy |
* Guice will only eagerly build singletons for the types it knows about. These are the types mentioned in your modules, plus the transitive dependencies of those types.
If the object is stateful, the scoping should be obvious. Per-application is
@Singleton
, per-request is @RequestScoped
, etc. If the object is stateless
and inexpensive to create, scoping is unnecessary. Leave the binding unscoped
and Guice will create new instances as they're required.
Singletons are popular in Java applications but they don't provide much value, especially when dependency injection is involved. Although singletons save object creation (and later garbage collection), initialization of the singleton requires synchronization; getting a handle to the single initialized instance only requires reading a volatile. Singletons are most useful for:
- stateful objects, such as configuration or counters
- objects that are expensive to construct or lookup
- objects that tie up resources, such as a database connection pool.
Classes annotated @Singleton
and @SessionScoped
must be threadsafe.
Everything that's injected into these classes must also be threadsafe.
Minimize mutability to limit the amount of state that
requires concurrency protection.
@RequestScoped
objects do not need to be threadsafe. It is usually an error
for a @Singleton
or @SessionScoped
object to depend on an @RequestScoped
one. Should you require an object in a narrower scope, inject a Provider
of
that object.
If you are testing a Guice module that uses scopes (especially
custom scopes) but don't
actually care about the scoping of the binding in the tests, you can use Guice's
Scopes.NO_SCOPE
to override a specific scope. NO_SCOPE
is an implementation
of Scope
that returns a new instance every time an object is requested.
Example test that binds BatchScoped
to NO_SCOPE
:
import com.google.inject.Scopes;
@RunWith(Junit4.class)
final class FooTest {
static class TestModule extends AbstractModule {
@Override
protected void configure() {
bindScope(BatchScoped.class, Scopes.NO_SCOPE);
}
}
}
BatchScoped
in the above example is a custom
scope annotation.
-
User's Guide
-
Integration
-
Extensions
-
Internals
-
Releases
-
Community