6

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

这个 $ injectorsget()的功能不同。

$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();
                }
            }

        });

这是一个可以玩的笨蛋

谁能解释为什么有两种不同的注射器?

以及如何使用 provider/config 中的 $injector 来注入服务(当然是在服务初始化之后)?

PS我使用角度1.3.13

4

3 回答 3

8

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

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

一个是配置阶段的 $injector(只有提供程序和常量可访问),一个是运行阶段的 $injector。混淆可能是您认为 $injector 会修改自身以包含新内容,因为它跨越了从配置到运行的界限,但事实并非如此。它们是两个独立的(尽管相关的)对象,具有自己的实例缓存。

这种二分法的更深入的原因可能来自对 $injector 内部结构的深度学习,但它似乎已经被 DRY-ed 非常硬核,并且这两种类型的注入器具有几乎所有相同的行为,除了如何他们处理实例缓存中的“缓存未命中”。

我们将在 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){}])
于 2015-04-29T03:25:53.913 回答
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 块的引用:

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

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

于 2015-04-28T08:22:12.123 回答
0

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

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

于 2017-04-26T12:14:33.887 回答