Skip to content

Scope Resolution

Stéphane Nicolas edited this page May 8, 2016 · 17 revisions

Scope resolution

Scope resolution is one of the most important part of ToothPick. It's intuitive in some extent.

The core idea is that scopes are always bubbled up when resolving an injection, and looking up for a binding. Toothpick never goes down the scope tree.

At runtime, when ToothPick will create the injection graph to create an instance of a class and its dependencies, Toothpick will crash if a dependency of this class can't be found in the scope itself or its parent scopes.

Here is an example :

//Example of scopes during the life of an Android application

+----------------------------------------------------------------------------------+  Resolution
| +---------------------------------------------------------------+                |  space
| |  application scope = @Singleton :                             | Resolution     |  for Activity
| |        /                    - Scope --> (application)         | space          |  scope
| |       /                     - IDisplay --> DisplayImpl1       | for @Singleton |
| |      /                      - FooSingleton --> (FooSingleton) | scope          |  
| +-----/---------------------------------------------------------+                |
|   activity scope = @ActivitySingleton :                                          |
|                               - Scope --> (activity)                             |
|                               - IDisplay --> DisplayImpl2                        |
|                               - FooActivitySingleton --> (FooActivitySingleton)  |
+----------------------------------------------------------------------------------+

Let's define :

class DisplayImpl1 {@Inject Scope scope}
class DisplayImpl2 {@Inject Scope scope}
@Singleton class FooSingleton {@Inject IDisplay display; @Inject Scope scope}
@ActivitySingleton class FooActivitySingleton {@Inject Scope s; @Inject IDisplay display;}
@Singleton class FooSingletonError {@Inject FooActivitySingleton foo;}

In the table below, we

obj getInstance(obj, application scope) getInstance(obj, activity scope)
new Scope() git status List all new or modified files
git diff Show file differences that haven't been staged
  • class Foo is not bound to a scope (un-scoped). It can be used in any scope. ToothPick.inject(foo, s) will always return foo.scope = s.
  • FooSingleton can use an un-scoped dependency such as Foo. ToothPick.inject(fooSingleton, s) will always return fooSingleton.foo.scope = application scope.
  • FooSingleton2 can use a dependency in its own scope.
  • FooActivitySingleton can use an un-scoped dependency such as Foo. ToothPick.inject(fooActivitySingleton, s) will always return fooActivitySingleton.foo.scope = activity scope.
  • FooActivitySingleton2 can use a dependency in its own scope.
  • FooActivitySingleton3 can use a dependency in any parent scope of its own scope. ToothPick.inject(fooActivitySingleton, s) will always return fooActivitySingleton3.foo.scope = activity scope but also fooActivitySingleton3.fooSingleton.scope = application scope

Indeed, there are 2 DAGs in Toothpick :

  • the injection dependency relation between classes is oriented (a class Foo uses an injected instance of class Bar = class Foo {@Inject Bar});
  • the parent relation between scopes is oriented (an activity scope uses the parent application scope).