可以用$ provide.decorator装饰Angular $注入器吗?

Joh*_*soe 20 angularjs

也许这是一个可怕的想法,但如果是,那么请告诉我为什么然后假装这是一个学术练习,不会在生产中看到光明.

我想为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;
    };
})();
Run Code Online (Sandbox Code Playgroud)

具体错误是:

未捕获错误:[$ injector:modulerr]由于以下原因无法实例化模块应用程序:错误:[$ injector:unpr]未知提供程序:$ injectorProvider

art*_*iak 13

答案是不.


$provide.decorator用于拦截服务创建 - 这就是为什么它从.config块调用,当仍有时间配置所有服务时,因为它们都没有创建.$provide.decorator基本上得到了Provider服务,并$get与新交付的交换decorFn.

$injector不像其他服务.它创建,如在第一个步骤bootstrapping的应用-的方式之前app.config被调用.[看看函数:bootstrapcreateInjector角度源代码]

但是,嘿,你可以通过稍微调整源代码很容易实现你的目标:-)特别注意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;
  }]);
Run Code Online (Sandbox Code Playgroud)

PLNKR


Kay*_*ave 12

您不能在$ injector上使用Angular装饰器服务.正如Artur所说,$injector与其他服务有点不同.但我们可以创建自己的装饰器.

为什么我们不能使用Angular的装饰器

在代码级别,问题是$injector没有构造函数 - 没有$injectorProvider.

例如,这两个都返回true:

$injector.has('$location');
$injector.has('$locationProvider') 
Run Code Online (Sandbox Code Playgroud)

但是,这会返回true:

$injector.has('$injector')
Run Code Online (Sandbox Code Playgroud)

这返回false:

$injector.has('$injectorProvider')
Run Code Online (Sandbox Code Playgroud)

当我们查看Angular装饰器函数时,我们看到了重要性:

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});
   };
}
Run Code Online (Sandbox Code Playgroud)

providerSuffix = 'Provider'
Run Code Online (Sandbox Code Playgroud)

所以Angular装饰器期望在服务的构造函数(serviceName + providerSuffix)上运行.实际上$injectorProvider,因为我们没有一个我们不能使用装饰器.

我们所能做的是覆盖角度喷射的get功能,通过更换喷油器的默认自己get与一个调用原始,角定义,get其次是我们的职责.

我们将这应用于$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;
    }
}]);
Run Code Online (Sandbox Code Playgroud)

您将看到正在注入的提供程序是第一个扩充,因此app.value('aValue', 'something');产生以下日志语句:

injector called  ["aValueProvider"]
Run Code Online (Sandbox Code Playgroud)

演示小提琴