Top Description Fields Constructors Methods
com.google.inject.internal

public Class SingletonScope

extends Object
implements Scope
Class Inheritance
All Implemented Interfaces
com.google.inject.Scope
Imports
com.google.common.base.Preconditions, com.google.common.collect.ImmutableList, .Iterables, .ListMultimap, com.google.inject.Injector, .Key, .Provider, .ProvisionException, .Scope, .Scopes, .Singleton, com.google.inject.internal.CycleDetectingLock.CycleDetectingLockFactory, com.google.inject.spi.Dependency, .Message, java.util.Formatter, .List, javax.annotation.Nullable

One instance per Injector. Also see @Singleton.

Introduction from the author: Implementation of this class seems unreasonably complicated at the first sight. I fully agree with you, that the beast below is very complex and it's hard to reason on how does it work or not. Still I want to assure you that hundreds(?) of hours were thrown into making this code simple, while still maintaining Singleton contract.

Anyway, why is it so complex? Singleton scope does not seem to be that unique.

  1. Guice has never truly expected to be used in multi threading environment with many Injectors working alongside each other. There is almost no code with Guice that propagates state between threads. And Singleton scope is The exception.
  2. Guice supports circular dependencies and thus manages proxy objects. There is no interface that allows user defined Scopes to create proxies, it is expected to be done by Guice. Singleton scope needs to be able to detect circular dependencies spanning several threads, therefore Singleton scope needs to be able to create these proxies.
  3. To make things worse, Guice has a very tricky definition for a binding resolution when Injectors are in in a parent/child relationship. And Scope does not have access to this information by design, the only real action that Scope can do is to call or not to call a creator.
  4. There is no readily available code in Guice that can detect a potential deadlock, and no code for handling dependency cycles spanning several threads. This is significantly harder as all the dependencies in a thread at runtime can be represented with a list, where in a multi threaded environment we have more complex dependency trees.
  5. Guice has a pretty strong contract regarding Garbage Collection, which often prevents us from linking objects directly. So simple domain specific code can not be written and intermediary id objects need to be managed.
  6. Guice is relatively fast and we should not make things worse. We're trying our best to optimize synchronization for speed and memory. Happy path should be almost as fast as in a single threaded solution and should not take much more memory.
  7. Error message generation in Guice was not meant to be used like this and to work around its APIs we need a lot of code. Additional complexity comes from inherent data races as message is only generated when failure occurs on proxy object generation. Things get ugly pretty fast.
Author
timofeyb (Timothy Basanov)
See Also
scope(Key, Provider), CycleDetectingLock

Field Summary

Modifier and TypeField and Description
private static final CycleDetectingLock.CycleDetectingLockFactory<Key<?>>
cycleDetectingLockFactory

Allows us to detect when circular proxies are necessary.

private static final Object
NULL

A sentinel value representing null.

Constructor Summary

AccessConstructor and Description
public

Method Summary

Modifier and TypeMethod and Description
public <T> Provider<T>
scope(final Key<T>
binding key
key
,
final Provider<T>
locates an instance when one doesn't already exist in this scope.
creator
)

Implements com.google.inject.Scope.scope.

Provides singleton scope with the following properties:
  • creates no more than one instance per Key as a creator is used no more than once
  • result is cached and returned quickly on subsequent calls
  • exception in a creator is not treated as instance creation and is not cached
  • creates singletons in parallel whenever possible
  • waits for dependent singletons to be created even across threads and when dependencies are shared as long as no circular dependencies are detected
  • returns circular proxy only when circular dependencies are detected
  • aside from that, blocking synchronization is only used for proxy creation and initialization
public String
toString()

Overrides java.lang.Object.toString.

Implements com.google.inject.Scope.toString.

A short but useful description of this scope.
Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAllwaitwaitwait

Field Detail

cycleDetectingLockFactoryback to summary
private static final CycleDetectingLock.CycleDetectingLockFactory<Key<?>> cycleDetectingLockFactory

Allows us to detect when circular proxies are necessary. It's only used during singleton instance initialization, after initialization direct access through volatile field is used.

NB: Factory uses Keys as a user locks ids, different injectors can share them. Cycles are detected properly as cycle detection does not rely on user locks ids, but error message generated could be less than ideal.

NULLback to summary
private static final Object NULL

A sentinel value representing null.

Constructor Detail

SingletonScopeback to summary
public SingletonScope()

Method Detail

scopeback to summary
public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator)

Implements com.google.inject.Scope.scope.

Provides singleton scope with the following properties:

  • creates no more than one instance per Key as a creator is used no more than once
  • result is cached and returned quickly on subsequent calls
  • exception in a creator is not treated as instance creation and is not cached
  • creates singletons in parallel whenever possible
  • waits for dependent singletons to be created even across threads and when dependencies are shared as long as no circular dependencies are detected
  • returns circular proxy only when circular dependencies are detected
  • aside from that, blocking synchronization is only used for proxy creation and initialization
Parameters
key:Key<T>

Doc from com.google.inject.Scope.scope.

binding key

creator:Provider<T>

Doc from com.google.inject.Scope.scope.

locates an instance when one doesn't already exist in this scope.

Returns:Provider<T>

Doc from com.google.inject.Scope.scope.

a new provider which only delegates to the given unscoped provider when an instance of the requested object doesn't already exist in this scope

Annotations
@Override
See Also
CycleDetectingLockFactory
toStringback to summary
public String toString()

Overrides java.lang.Object.toString.

Implements com.google.inject.Scope.toString.

Doc from com.google.inject.Scope.toString.

A short but useful description of this scope. For comparison, the standard scopes that ship with guice use the descriptions "Scopes.SINGLETON", "ServletScopes.SESSION" and "ServletScopes.REQUEST".

Returns:String

Doc from java.lang.Object.toString.

a string representation of the object

Annotations
@Override