-
Notifications
You must be signed in to change notification settings - Fork 113
Scoped & Unscoped Bindings
In toothpick there are 2 kinds of bindings :
- unscoped bindings
- scoped bindings
In the 2 following sections, we will use a common example class and see how scoped and unscoped bindings differ when injecting it :
class A {
@Inject IFoo foo1;
@Inject IFoo foo2;
}
class Foo {
@Inject Scope s;
}
bind(IFoo.class).to(Foo.class) is a simple association IFoo --> Foo. An unscoped binding expresses no constraints on the creation of the Foo instances, as opposed to a scope binding. An unscoped binding is said to belong to a given scope.
Basically, when a scope defines bind(IFoo.class).to(Foo.class), it means that in this scope and its children scopes : @Inject IFoo foo will return a Foo.
The scope tree used in this example will be :
Scope s0 : Scope --> S0
\
\
Scope S1 : Scope --> S1 & IFoo --> Foo
\
\
Scope S2 : Scope --> S2
(Remember that the binding of class Scope is always overridden by all scopes.)
Then using the classes A & Foo & the scope tree defined above, we would have :
-
Toothpick.inject(new A(), S0): There is no binding to injectIFooas the binding is only defined in child scope S1. The injection ofnew A()inS0will fail with an appropriate error message. -
Toothpick.inject(new A(), S1): The injection ofnew A()inS1will result in the following : -
a.foo1will be an instance ofFoo; -
a.foo2will be an instance ofFoo, different froma.foo1; -
a.foo1.scope&a.foo2.scopewill beS1. -
Toothpick.inject(new A(), S2): The injection ofnew A()inS2will result in the following : -
a.foo1will be an instance ofFoo; -
a.foo2will be an instance ofFoo, different froma.foo1; -
a.foo1.scope&a.foo2.scopewill beS2.
A binding is scoped when we call its method scope(). bind(IFoo.class).to(Foo.class).scope()
express more than the simple association IFoo --> Foo. A scoped bindings means :
- there is an association
IFoo --> Foo, in the same way as an unscoped binding does; - AND the same instance of
Foois recycled/reused for each injection ofIFoo - AND the instance of
Foowill be created inside the scope. All the dependencies ofFoowill have to be found at runtime in the scope where the binding is scoped or in its parent scopes.
A scoped binding is said to be scoped in a given scope. We note scoped bindings IFoo --> (Foo) with the target of the binding in parenthesis.
The scope tree used in this example will be :
Scope s0 : Scope --> S0
\
\
Scope S1 : Scope --> S1 & IFoo --> (Foo) // <-- scoped binding
\
\
Scope S2 : Scope --> S2
(Remember that the binding of class Scope is always overridden by all scopes.)
Then using the classes A & Foo & the scope tree defined above, we would have :
-
Toothpick.inject(new A(), S0): There is no binding to injectIFooas the binding is only defined in child scope S1. The injection ofnew A()inS0will fail with an appropriate error message. -
Toothpick.inject(new A(), S1): The injection ofnew A()inS1will result in the following : -
a.foo1will be an instance ofFoo; -
a.foo2will be the same instance ofFooasa.foo1; -
a.foo1.scope&a.foo2.scopewill beS1. -
Toothpick.inject(new A(), S2): The injection ofnew A()inS2will result in the following : -
a.foo1will be an instance ofFoo; -
a.foo2will be the same instance ofFooasa.foo1, and the same instance as whenAwas injected inS1. -
a.foo1.scope&a.foo2.scopewill beS1.
The last line a.foo1.scope = a.foo2.scope = S1 is the most important.
Defining a scoped binding IFoo --> (Foo) means that Foo, the target of the injection, must fulfill all its dependencies in the scope where it is scoped or the parents of this scopes OR the dependencies should be unscoped.
//space of creation of `Foo` instances in the case of a scoped binding in `S1`.
+--------------------------------------------------------------------+
| Scope s0 : Scope --> S0 |
| \ |
| \ |
| Scope S1 : Scope --> S1 & IFoo --> (Foo) // <-- scoped binding |
| \ |
+-----------\--------------------------------------------------------+
\
Scope S2 : Scope --> S2
All the bindings defined in children scopes of S1 are not taken into account : Foo instances must exist, as well as all their transitive dependencies, in S0 & S1. And the instances of Foo created will be recycled in S1 and the scope below S1.
ToothPick will enforce this constraint and will check that a scoped binding doesn't require any dependency that is scoped in a children scope.