AngularJSでServiceがControllerへDIされるまでの流れをざっくり見てみる
引き続きAngularJSのDeveloper Guideを読んで勉強しています。
今回はInjecting Services Into Controllersのサンプルコードで、Serviceが登録されてからControllerの中で利用されるまでAngularJSの中では何が起こっているのか、関係のあるところだけ辿って見てみました。
サンプルコードは以下の通り。
angular. module('MyServiceModuleDI', []). factory('notify', ['$window', function(win) { var msgs = []; return function(msg) { msgs.push(msg); if (msgs.length == 3) { win.alert(msgs.join("\n")); msgs = []; } }; }]); function myController($scope, notify) { $scope.callNotify = function(msg) { notify(msg); }; }
angular.module('MyServiceModuleDI', [])から時系列順に見てみます。
angular.module('MyServiceModuleDI', [])
angular.moduleは、AngularPublic.jsの(setupModuleLoader(window))で定義されます。angular.module関数の中身は、loader.jsで定義されているmoduleという関数です。
angular.module('MyServiceModuleDI', [])を実行すると、'MyServiceModuleDI'という名前でmoduleInstanceというオブジェクトがキャッシュされ、また戻り値として帰ってきます。
moduleInstanceは以下の様なプロパティを持っています。
var moduleInstance = { _invokeQueue: invokeQueue, _runBlocks: runBlocks, requires: requires, name: name, provider: invokeLater('$provide', 'provider'), factory: invokeLater('$provide', 'factory'), service: invokeLater('$provide', 'service'), value: invokeLater('$provide', 'value'), constant: invokeLater('$provide', 'constant', 'unshift'), filter: invokeLater('$filterProvider', 'register'), controller: invokeLater('$controllerProvider', 'register'), directive: invokeLater('$compileProvider', 'directive'), config: config, run: function(block) { runBlocks.push(block); return this; } };
.factory('notify', ['$window', function(win) { (省略) }
factoryは先ほど出てきたmoduleInstanceのプロパティです。factory関数を実行すると、moduleInstanceのinvokeQueueの中に引数として渡したServiceのファクトリなどが追加されます。コードで表すと以下の様な感じです。
invokeQueue.push(['$provide', 'factory', ['notify', ['$window', function(win) { (省略) }]]]);
createInjector(modules)
DOMContentLoadedイベントが発生するとアプリケーションのBootstrapプロセスに入ります。この中でcreateInjector(modules)が実行されるとModuleがロードされ、ServiceをControllerへDIすることが出来るようになります。
createInjectorはinjector.jsで定義されている関数です。createInjectorを実行すると内部的にproviderInjectorとinstanceInjectorが作成されます。
Moduleがロードされる過程では、moduleInstanceのinvokeQueueに追加されたServiceのファクトリがproviderInjectorに登録されます。'notify'の場合以下の様な感じです。
return providerCache['notify' + "Provider"] = { $get: ['$window', function(win) { (省略) }] };
ServiceをControllerへDIする際にはinstanceInjectorが持っているキャッシュからServiceを取得しますが、キャッシュにない場合はproviderInjectorに登録されたファクトリを実行してServiceを取得します。
$injector.instantiate(constructor, locals);
アプリケーションのBootstrapプロセスでは$injector.instantiateが実行され、ServiceがControllerへDIされます。instantiateもinjector.jsで定義されています。