也许这是一个可怕的想法,但如果是,那么请告诉我为什么,然后假装这是一个学术练习,不会在生产中看到光明。
我想为Angular $ injector服务添加一些逻辑,以监视何时将某些服务注入其他服务。由于Angular似乎提供了一种装饰服务的机制,我认为这将是一种方法。但是,以下代码会引发错误。
(function () {
'use strict';
var app = angular.module('app');
app.config(['$provide', function ($provide) {
$provide.decorator('$injector', ['$log', '$delegate', addLoggingToInjector]);
}]);
function addLoggingToInjector($log, $delegate) {
var baseInstantiate = $delegate.instantiate;
var baseInvoke = $delegate.invoke;
$delegate.instantiate = function (type, locals) {
// $log.debug('Calling $injector.instantiate');
baseInstantiate(type, locals);
};
$delegate.invoke = function (fn, self, locals) {
// $log.debug('Calling $injector.invoke');
baseInvoke(fn, self, locals);
};
return $delegate;
};
})();
具体错误是:
未捕获错误:[$ injector:modulerr]无法实例化模块应用 由于:错误:[$ injector:unpr]未知提供者:$ injectorProvider
答案 0 :(得分:13)
答案是:不。
$provide.decorator
用于拦截服务创建 - 这就是为什么从.config
块调用它时,仍有时间配置所有服务,因为没有他们已经创建了。 $provide.decorator
基本上获得服务的Provider
,并使用新发送的$get
交换decorFn
。
$injector
与其他服务不同。它是{{1>} bootstrapping
应用程序的第一个步骤创建的,在调用app.config
之前。 [查看角度源代码中的函数:bootstrap
和createInjector
]
但是,嘿,你可以通过稍微调整一下源代码很容易实现你的目标:-)特别要看function invoke(fn, self, locals)
。
更新我从@KayakDave获得了一些灵感。实际上你不必深入挖掘源代码本身。您可以使用以下模式观察对$injector
方法的任何调用:
app.config(['$injector', function ($injector) {
$injector.proper =
{
get : $injector.get,
invoke : $injector.invoke,
instantiate : $injector.instantiate,
annotate : $injector.annotate,
has : $injector.has
}
function getDecorator(serviceName)
{
console.log("injector GET: ", serviceName);
return this.proper.get(serviceName);
}
function invokeDecorator(fn, self, locals)
{
console.log("injector INVOKE: ", fn, self, locals);
return this.proper.invoke(fn, self, locals);
}
function instantiateDecorator(Type, locals)
{
console.log("injector INSTANTIATE: ", Type, locals);
return this.proper.instantiate(Type, locals);
}
function annotateDecorator (fn)
{
console.log("injector ANNOTATE: ", fn);
return this.proper.annotate(fn);
}
function hasDecorator(name)
{
console.log("injector HAS: ", name);
return this.proper.has(name);
}
$injector.get = getDecorator;
$injector.invoke = invokeDecorator;
$injector.instantiate = instantiateDecorator;
$injector.annotate = annotateDecorator;
$injector.has = hasDecorator;
}]);
答案 1 :(得分:12)
您不能在$ injector上使用Angular装饰器服务。正如Artur所说,$injector
与其他服务有点不同。但我们可以创建自己的装饰器。
在代码级别,问题是$injector
没有构造函数 - 没有$injectorProvider
。
例如,这两个都返回true:
$injector.has('$location');
$injector.has('$locationProvider')
但是,这会返回true:
$injector.has('$injector')
这会返回false:
$injector.has('$injectorProvider')
当我们查看Angular decorator函数时,我们看到了重要性:
function decorator(serviceName, decorFn) {
var origProvider = providerInjector.get(serviceName + providerSuffix),
orig$get = origProvider.$get;
origProvider.$get = function() {
var origInstance = instanceInjector.invoke(orig$get, origProvider);
return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
};
}
和
providerSuffix = 'Provider'
因此Angular装饰器希望对服务的构造函数(serviceName + providerSuffix
)进行操作。实际上,因为我们没有$injectorProvider
我们不能使用装饰。
我们可以做的是通过将注入器的默认get
替换为调用原始的Angular定义的get
后跟我们的函数来替换Angular注入器的get
function。
我们会将其应用于$injector
而非不存在的$injectorProvider
,如下所示:
app.config(['$provide','$injector', function ($provide,$injector) {
// The function we'll add to the injector
myFunc = function () {
console.log("injector called ", arguments);
};
// Get a copy of the injector's get function
var origProvider = $injector,
origGet = origProvider.get;
//Override injector's get with our own
origProvider.get = function() {
// Call the original get function
var returnValue = origGet.apply(this, arguments);
// Call our function
myFunc.apply(this,arguments);
return returnValue;
}
}]);
您将看到正在注入的提供程序是第一个扩充,因此app.value('aValue', 'something');
会生成以下日志语句:
injector called ["aValueProvider"]