Deep Dive into Ninject

Aujourd’hui, j’ai organisé un Brown Bag Meeting dont le sujet était un Deep Dive into Ninject. Bon, personne ne mangeait parce qu’il était 15h00 mais c’est pour l’occasion de faire une pub au Brown Bag Meetings. Je trouve que c’est une idée très intéressante.

Pour en revenir au sujet initial de l’article: Ninject. Ninject est un moteur d’injection de dépendances pour ceux qui ne connaissent pas. Nous l’utilisons aussi pour faire de l’AOP avec des intercepteurs maison.

Voyons oune poquito comment c’est sous l’capot

commercial-waste-management-ninjas

Quelques billes sur le ninja

Ninject est un framework d’injection de dépendances. Je l’ai déjà dit. Il propose nativement une Fluent interface qui le rend Developer-friendy.

La librairie est très légère: 124 ko pour le noyau (sans les add-ons)

C’est l’un des DR les plus véloces, car il se base sur des générations de proxy @runtime (enfin, il se warmup au démarrage de l’appli et après gogogo)

A noter qu’il existe sur le web une page d’un blog qui recense des tests de performances moisis surles différents DR du marché. Cette page a à peu près 75 ans et les verisons testées aussi (je ne parlerai pas de la pertinence des tests (résoudre 4 milliards de fois une dépendance (qu’on pourrait mettre en cache) pour montrer 500 ms de différence… c’est un business case courant)

Il est extensible :). Le rêve. Les extensions sont déjà légions.

Deep Dive into Modules, and Bindings

Regardons de plus près les classes qui héritent de la classe NinjectModule : Si vous utilisez Ninject, vous en avez forcément défini. Ce sont les classes qui définissent les bindings entre les contrats et les implémentations que vous voulez injecter.

Par exemple:

DeepDiveIntoNinject_BindingConfiguration

Que se passe t’il lorsque cette ligne est exécutée? On passe par la classe BindingRoot, qui utilise un BindingBuilder. Celui-ci va contenir entre autres, la classe Binding, qui contient  le type du contrat pour lequel on va enregistrer les implémentations possibles, via les classes StandardProvider, et la classe BindingConfigurationBuilder.

La classe BindingConfigurationBuilder, comme son nom l’indique, implémente un pattern Builder qui est responsable en partie de la Fluent interface de Ninject. C’est cette classe qui va permettre de construire le binding entre le contrat et les implémentations en ajoutant les conditions que vous souhaitez (When(), WithParameter(), WithConstructor()…).

Voici une méthode pour ceux qui ne connaîtraient pas ce pattern, c’est très intéressant:

DeepDiveIntoNinject_BindingConfiguurationBuilder

Deep Dive into Kernels

Le noyau est la classe qui gère toute l’injection de dépendances. Une fois instancié, avec la configuration de bindings qui va bien, le kernel va injecter dans les contrats les implémentations que nous attendons.

La résolution se fait via un appel à la méthode Get<TOut>() du kernel. On continue la descente pour arriver à la méthode qui nous intéresse le plus, la résolution. A noter que, les différents bindings créés précédemment ont été collectés. Là, on va les faire fonctionner pour récupérer les instances attendues.

DeepDiveIntoNinject_KernelBaseResolve

 

Le premier if regarde s’il n’existerait par hasard, pas de binding pour ce contrat, et tente sa chance si c’est le cas. Le second if vérifie si l’on souhaite une unique réponse d’implémentation pour le contrat, ou non. On s’intéresse donc ici à ce cas d’instanciation unique (on a fait un Get<TOut>() donc on attend un object en réponse)

Que fait donc le second if? Ce qu’il faut comprendre se passe en deux étapes.

  1. Création du contexte
  2. Résolution

La création du contexte:

Les propriétés importantes contenues par le Context sont (ok, le kernel et la request, mais en fait on s’en fout, ça fait un moment qu’on se les traîne) le cache, le planner, et le pipeline.

  • le cache va servir à stocker les précédentes résolutions afin de pouvoir les réutiliser directement au prochain appel. C’est pour la perf. Enfin c’est un cache en somme.
  • le pipeline gère l’activation d’une instance. Il contient les stratégies d’activation ou de désactivation des instances et est utilisé pour activer ou désactiver les instances présentes en cache.
  • le planner génère les plans qui vont permettre de savoir comment activer les instances.  Il contient les différentes stratégies d’exécution possibles (PropertyInjectionDirective, ConstructorInjectionDirective et MethodInjectionDirective)

A noter, chose marrante, que le planner, le pipeline et le cache sont retrouvés par Ninject par résolution de dépendances :). Celles-ci ont été configurées lors de l’instanciation du StandardKernel. C’est aussi le signe d’une possibilité de customisation complète du noyau et des stratégies d’activation, mais c’est une autre histoire, et un autre article 🙂

La résolution

La résolution va d’emblée regarder dans le cache s’il y a une instance présente pour le contrat.

DeepDiveIntoNinject_ContextResolve

Si elle ne trouve rien, elle va tenter la création de l’instance. Cela se passe dans le StandardProvider après résolution de la méthode d’activation, sur l’une des directives d’injection vues précédemment.

 

Advertisements

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s