Author: limpbizkit
Date: Thu Jan 1 16:49:21 2009
New Revision: 779
Modified:
wiki/Scopes.wiki
Log:
Edited wiki page through web user interface.
Modified: wiki/Scopes.wiki
==============================================================================
--- wiki/Scopes.wiki (original)
+++ wiki/Scopes.wiki Thu Jan 1 16:49:21 2009
@@ -1,36 +1,15 @@
-#summary Overview of writing custom scopes for Guice
+=Scopes=
-= Introduction =
+==Choosing a scope==
+If the object is *stateful*, the scoping should be obvious.
Per-application is `...@singleton`, per-request is `...@requestscoped`, etc.
You
may need to write a [CustomScopes custom scope]. 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.
-Scopes are one of Guice's most powerful features.
+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), getting a
handle to the single instance requires synchronization. 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.
-= Applying Scopes =
-
-Prefer to apply scopes using an annotation on the implementing class. This
provides helpful information to the code's maintainer. For example,
`...@singleton` indicates that a class must be threadsafe.
-
-Scopes are applied to the binding source, not the binding target. For
example, suppose we have a class `Applebees` that implements both `Bar` and
`Grill` interfaces. These bindings will 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 of our implementation class to be created, use a `...@singleton`
annotation on the declaration for that class. Or add yet another binding:
-{{{
- bind(Applebees.class).in(Singleton.class);
-}}}
-This binding makes the other two `.in(Singleton.class)` clauses above
unnecessary.
-
-Prefer to bind with the scope annotation rather than the scope instance:
-{{{
- // good:
- bind(Foo.class).to(RealFoo.class).in(ServletScopes.REQUEST);
-
- // better:
- bind(Foo.class).to(RealFoo.class).in(RequestScoped.class);
-}}}
-This way you can reuse the module in a non-servlets environment by
specifying a different scope to implement for `RequestScoped.class`. Even
better, use the `...@requestscoped` annotation on the implementation class.
-
-= Eager Singletons =
+==Eager Singletons==
Guice can build your singletons either eagerly or lazily. 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* ||
@@ -41,183 +20,33 @@
`*` 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.
-= Custom Scopes =
-It is generally recommended that users *do not* write their own custom
scopes -- the built-in scopes should be sufficient for most applications.
If you're writing a web application, the `ServletModule` provides simple,
well tested scope implementations for HTTP requests and HTTP sessions.
+==Scopes and Concurrency==
+Classes annotated `...@singleton` and `...@sessionscoped` *must be
threadsafe*.
Everything that's injected into these classes must also be threadsafe. Use
[MinimizeMutability minimize mutability] to limit the amount of state that
requires concurrency protection.
-Creating custom scopes is a multistep process:
- # Define a scoping annotation
- # Implementing the `Scope` interface
- # Attaching the scope annotation to the implementation
- # Triggering scope entry and exit
+...@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.
-==Defining a scoping annotation==
-The scoping annotation identifies your scope. You'll use it to annotate
Guice-constructed types, `...@provides` methods, and in the `in()` clause of a
bind statement. Copy-and-customize this code to define your scoping
annotation:
-{{{
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.TYPE;
-import java.lang.annotation.Retention;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-import java.lang.annotation.Target;
-...@target({ TYPE, METHOD }) @Retention(RUNTIME) @ScopeAnnotation
-public @interface BatchScoped {}
-}}}
-*Tip:* If your scope represents a request or session (such as for SOAP
requests), consider using the `RequestScoped` and `SessionScoped`
annotations from Guice's servlet extension. Otherwise, you may import the
wrong annotation by mistake. That problem can be quite frustrating to debug.
+==Applying Scopes==
+Prefer to apply scopes using an annotation on the implementing class. This
provides helpful information to the code's maintainer. For example,
`...@singleton` indicates that a class must be threadsafe.
-==Implementing Scope==
-The scope interface ensures there's at most one type instance for each
scope instance. `SimpleScope` is a decent starting point for a per-thread
implementation. Copy this class into your project and tweak it to suit your
needs.
+Scopes are applied to the binding source, not the binding target. For
example, suppose we have a class `Applebees` that implements both `Bar` and
`Grill` interfaces. These bindings will allow for *two* instances of that
type, one for `Bar`s and another for `Grill`s:
{{{
-import static com.google.common.base.Preconditions.checkState;
-import com.google.common.collect.Maps;
-import com.google.inject.Key;
-import com.google.inject.OutOfScopeException;
-import com.google.inject.Provider;
-import com.google.inject.Scope;
-import java.util.Map;
-
-/**
- * Scopes a single execution of a block of code. Apply this scope with a
- * try/finally block: <pre> {...@code
- *
- * scope.enter();
- * try {
- * // explicitly seed some seed objects...
- * scope.seed(Key.get(SomeObject.class), someObject);
- * // create and access scoped objects
- * } finally {
- * scope.exit();
- * }
- * }</pre>
- *
- * The scope can be initialized with one or more seed values by calling
- * <code>seed(key, value)</code> before the injector will be called upon to
- * provide for this key. A typical use is for a servlet filter to
enter/exit the
- * scope, representing a Request Scope, and seed HttpServletRequest and
- * HttpServletResponse. For each key inserted with seed(), it's good
practice
- * (since you have to provide <i>some</i> binding anyhow) to include a
- * corresponding binding that will throw an exception if Guice is asked to
- * provide for that key if it was not yet seeded: <pre> {...@code
- *
- * bind(key)
- * .toProvider(SimpleScope.<KeyClass>seededKeyProvider())
- * .in(ScopeAnnotation.class);
- * }</pre>
- *
- * @author Jesse Wilson
- * @author Fedor Karpelevitch
- */
-public class SimpleScope implements Scope {
-
- private static final Provider<Object> SEEDED_KEY_PROVIDER =
- new Provider<Object>() {
- public Object get() {
- throw new IllegalStateException("If you got here then it means
that" +
- " your code asked for scoped object which should have been" +
- " explicitly seeded in this scope by calling" +
- " SimpleScope.seed(), but was not.");
- }
- };
- private final ThreadLocal<Map<Key<?>, Object>> values
- = new ThreadLocal<Map<Key<?>, Object>>();
-
- public void enter() {
- checkState(values.get() == null, "A scoping block is already in
progress");
- values.set(Maps.<Key<?>, Object>newHashMap());
- }
-
- public void exit() {
- checkState(values.get() != null, "No scoping block in progress");
- values.remove();
- }
-
- public <T> void seed(Key<T> key, T value) {
- Map<Key<?>, Object> scopedObjects = getScopedObjectMap(key);
- checkState(!scopedObjects.containsKey(key), "A value for the key %s
was " +
- "already seeded in this scope. Old value: %s New value: %s", key,
- scopedObjects.get(key), value);
- scopedObjects.put(key, value);
- }
-
- public <T> void seed(Class<T> clazz, T value) {
- seed(Key.get(clazz), value);
- }
-
- public <T> Provider<T> scope(final Key<T> key, final Provider<T>
unscoped) {
- return new Provider<T>() {
- public T get() {
- Map<Key<?>, Object> scopedObjects = getScopedObjectMap(key);
-
- @SuppressWarnings("unchecked")
- T current = (T) scopedObjects.get(key);
- if (current == null && !scopedObjects.containsKey(key)) {
- current = unscoped.get();
- scopedObjects.put(key, current);
- }
- return current;
- }
- };
- }
-
- private <T> Map<Key<?>, Object> getScopedObjectMap(Key<T> key) {
- Map<Key<?>, Object> scopedObjects = values.get();
- if (scopedObjects == null) {
- throw new OutOfScopeException("Cannot access " + key
- + " outside of a scoping block");
- }
- return scopedObjects;
- }
-
- /**
- * Returns a provider that always throws exception complaining that the
object
- * in question must be seeded before it can be injected.
- *
- * @return typed provider
- */
- @SuppressWarnings({"unchecked"})
- public static <T> Provider<T> seededKeyProvider() {
- return (Provider<T>) SEEDED_KEY_PROVIDER;
- }
-}
+ bind(Bar.class).to(Applebees.class).in(Singleton.class);
+ bind(Grill.class).to(Applebees.class).in(Singleton.class);
}}}
-
-==Binding the annotation to the implementation==
-You must attach your scoping annotation to the corresponding scope
implementation. Just like bindings, you can configure this in your module's
`configure()` method. Usually you'll also bind the scope itself, so
interceptors or filters can use it.
+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 of our implementation class to be created, use a `...@singleton`
annotation on the declaration for that class. Or add yet another binding:
{{{
-public class BatchScopeModule {
- public void configure() {
- SimpleScope batchScope = new SimpleScope();
-
- // tell Guice about the scope
- bindScope(BatchScoped.class, batchScope);
-
- // make our scope instance injectable
- bind(SimpleScope.class)
- .annotatedWith(Names.named("batchScope")
- .toInstance(batchScope);
- }
-}
+ bind(Applebees.class).in(Singleton.class);
}}}
+This binding makes the other two `.in(Singleton.class)` clauses above
unnecessary.
-==Triggering the Scope==
-`SimpleScope` requires that you manually enter and exit the scope. Usually
this lives in some low-level infrastructure code, like a filter or
interceptor. Be sure to call `exit()` in a `finally` clause, otherwise the
scope will be left open when an exception is thrown.
+Prefer to bind with the scope annotation rather than the scope instance:
{{{
- @Inject @Named("batchScope") SimpleScope scope;
+ // good:
+ bind(Foo.class).to(RealFoo.class).in(ServletScopes.REQUEST);
- /**
- * Runs {...@code runnable} in batch scope.
- */
- public void scopeRunnable(Runnable runnable) {
- scope.enter();
- try {
- // explicitly seed some seed objects...
- scope.seed(Key.get(SomeObject.class), someObject);
-
- // create and access scoped objects
- runnable.run();
-
- } finally {
- scope.exit();
- }
- }
-}}}
\ No newline at end of file
+ // better:
+ bind(Foo.class).to(RealFoo.class).in(RequestScoped.class);
+}}}
+This way you can reuse the module in a non-servlets environment by
specifying a different scope to implement for `RequestScoped.class`. Even
better, use the `...@requestscoped` annotation on the implementation class.
\ No newline at end of file
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"google-guice-dev" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/google-guice-dev?hl=en
-~----------~----~----~----~------~----~------~--~---