-
Notifications
You must be signed in to change notification settings - Fork 7
The Activation Process
Quite a bit of magic happens inside of Ninject when you call the kernel’s Get()
method. Here’s a rough sketch of the procedure:
- The binding of the type is resolved.
- The binding is used to create an activation plan, which describes the means by which the instance will be created and injected.
- An instance of the type is requested from the behavior defined in the activation plan. Depending on the behavior’s implementation, the behavior may simply return an existing instance of the type, in which case the rest of the activation process is not executed.
- If the behavior indicates that a new instance of the type is necessary, it is requested from the binding’s provider. The provider resolves any necessary arguments and calls the injection constructor.
- All properties are injected according to the activation plan.
- All methods are injected according to the activation plan.
- All fields are injected according to the activation plan.
- If the type implements the
IInitializable
interface, itsInitialize()
method is called. - If the type implements the
IStartable
interface, itsStart()
method is called.
This entire activation procedure can also be customized by altering the strategy chain used by the Activator kernel component. (More on customizations later.)
Returning to our earlier example, we had a WarriorModule
:
class WarriorModule : StandardModule {
public override Load() {
Bind<IWeapon>().To<Sword>();
Bind<Samurai>().ToSelf();
}
}
And we activated an instance of Samurai
like this:
class Program {
public static void Main() {
IKernel kernel = new StandardKernel(new WarriorModule());
Samurai warrior = kernel.Get<Samurai>();
warrior.Attack("the evildoers");
}
}
The activation process for the Samurai
type is as follows. The number to the left indicates the depth at which the activation is occurring, and some steps have been left out for clarity:
- (1) The kernel resolves the self-binding for the
Samurai
type that was defined in theWarriorModule
. - (1) The kernel asks the
TransientBehavior
that’s managingSamurai
to resolve an instance. - (1) The Samurai’s
TransientBehavior
asks the binding’s provider to create a new instance. - (2) The Samurai’s
StandardProvider
asks the kernel to resolve an instance ofIWeapon
. - (2) The kernel resolves the binding from
IWeapon
toSword
that was defined in theWarriorModule
. - (2) The kernel asks the
TransientBehavior
that’s managingSword
to resolve an instance. - (2) The @Sword@’s
TransientBehavior
asks the binding’s provider to create a new instance. - (2) The @Sword@’s
StandardProvider
calls the parameterless constructor ofSword
. - (2) The kernel returns the newly-created instance of
Sword
. - (1) The @Samurai@’s
StandardProvider
calls the injection constructor ofSamurai
, passing it the instance ofSword
as an argument. - (1) The kernel returns the newly-created instance of
Samurai
to the site of the method call inMain()
.
Whew! After all that, we end up with a Samurai
armed with a Sword
, and the call to its Attack()
method results in the same output as our original example: Chopped the evildoers clean in half. Although this might look like a lot of work just to get the same outcome, keep a few things in mind:
- All of this happens in the background, and you rarely have to think about what’s really happening. It Just Works.
- It actually adds much less overhead than it seems. Honest. :)
- This is an overly simplistic example. The more complex your application becomes, the more the power of Ninject will appear.
Continue reading: How Injection Works