AngularJS有两个不同的$ injectors

时间:2015-04-28 06:11:38

标签: angularjs dependency-injection angularjs-injector

今天我发现,注入配置或提供商的$injector与注入服务,工厂或控制器的$injector不同。

来自此$ injectors的get()函数的工作方式不同。

来自配置或提供商的

$injector,无法get()任何服务! $injector.get('myService')抛出Error: [$injector:unpr] Unknown provider: myService,但$injector.has('myService')返回true。这非常奇怪。

来自服务或控制器的

$injector正常工作。

以下是一个代码示例,以便更好地理解:

angular.module('app', [])

        .provider('myProvider', function ($injector) {
            this.$get = ['$injector', function (serviceInjector) {
                return {
                    providerInjector: $injector,
                    serviceInjector: serviceInjector
                };
            }];
        })

        .service('myService', function () {})

        .controller('myCtrl', function ($scope, myProvider) {
            var providerInjector = myProvider.providerInjector;
            var serviceInjector = myProvider.serviceInjector;

            console.log(providerInjector === serviceInjector); // -> false

            console.log(serviceInjector.has('myService')); // `serviceInjector` has `myService`
            console.log(getMyService(serviceInjector)); // `serviceInjector` can get `myService`

            console.log(providerInjector.has('myService')); // `providerInjector` has `myService` too!
            console.log(getMyService(providerInjector)); // but `providerInjector` can't get `myService`! =(

            function getMyService(injector) {
                try {
                    injector.get('myService');
                    return "OK";
                } catch (e) {
                    return e.toString();
                }
            }

        });

Here is a plunker to play

有人可以解释为什么有两种不同的注射器吗?

如何从provider / config使用$ injector注入服务(当然,在服务初始化之后)?

P.S。我使用angular 1.3.13

3 个答案:

答案 0 :(得分:8)

我在github上发现了这个问题:https://github.com/angular/angular.js/issues/5559

  

在config函数中,$ injector是提供程序注入器,其中在run函数中,$ injector是实例注入器。

     

配置阶段的$ inject(只有提供者和常量可访问),以及运行阶段的$ injector。混淆可能是你认为$ injector修改自己以包含新的东西,因为它跨越从配置到运行的线,但这不是真的。它们是两个独立的(尽管是相关的)对象,具有自己的实例缓存。

     

这种二分法的一个更深层次的原因可能来自对$ injector注射器内部的深入学习,但似乎它已经被干预了相当硬核,并且两种类型的注射器几乎共享相同的行为,除了他们如何处理"缓存未命中"在他们的实例缓存中。

     

我们将在v2中对喷油器进行大修,因此将在那里得到修复(摆脱配置阶段是喷油器v2的目标之一)。

看起来确实有两种不同的注入器,角度开发人员不会修复这种行为(在版本< 2.0中)。由于某种原因,没有人在$ injector文档中添加关于该方面的注释。

我无法找到一种方法如何在没有hacky技巧的情况下在配置块中真正获得实例注入器。所以,我写了一个可爱的提供者来解决这类问题。

.provider('instanceInjector', function () {

    var instanceInjector;

    function get() {
        return instanceInjector;
    }

    function exists() {
        return !!instanceInjector;
    }

    angular.extend(this, {
        get: get,
        exists: exists
    });

    this.$get = function ($injector) {
        instanceInjector = $injector;

        return {
            get: get,
            exists: exists
        };
    }
})

// We need to inject service somewhere.
// Otherwise $get function will be never executed
.run(['instanceInjector', function(instanceInjector){}])

答案 1 :(得分:1)

确定。看完你的评论后,这是我的答案。

我在plunk中编辑了代码以使其工作,在调用providerInjector.get()时,代码应如下所示:

$scope.getMyServiceFromProviderInjector = function () {
        try {
                 myProvider.providerInjector.get('myServiceProvider');//here is change in provider name
                 return "OK";
            } catch (e) {
                 return e.toString();
            }
   };

根据angular docs,以下引用了config和run blocks:

  
      
  • 配置块 - 在提供程序注册和配置阶段执行。只有提供者和常量   可以注入配置块。这是为了防止   在服务完全之前意外实例化服务   配置。
  •   
  • 运行块 - 在创建注入器后执行并用于启动应用程序。只有实例和常量才可以   注入运行块。这是为了防止进一步的系统   应用程序运行时的配置。
  •   

这只是意味着,您无法在配置块中获取服务实例。

答案 2 :(得分:0)

我前面写了这篇文章,它解释了AngularJS的两个注入器的生命周期,即providerInjector和instanceInjector。

http://agiliq.com/blog/2017/04/angularjs-injectors-internals/