AngularJS 两个不同的 $injector

Posted

技术标签:

【中文标题】AngularJS 两个不同的 $injector【英文标题】:AngularJS two different $injectors 【发布时间】:2015-07-06 20:13:44 【问题描述】:

今天发现,$injector 注入到 config 或 provider 与 $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 来注入服务(当然是在服务初始化之后)?

附:我使用角度 1.3.13

【问题讨论】:

你的问题有点不清楚。你想在提供者/配置中实现什么?能给个用例吗? 用例?例如:通过配置部分的 $injector 获取服务。或提供者。当然是在服务初始化之后。就我而言(显示起来非常复杂),我在配置部分向提供者添加了回调。在回调中我想使用我的服务。 DI 帮不了我,所以我使用 $injector。当服务已经初始化时,将从控制器调用此回调。 【参考方案1】:

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

在config函数中,$injector是provider注入器,而在run函数中,$injector是instance注入器。

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

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

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

似乎真的有两个不同的注入器,并且 Angular 开发人员不会修复这种行为(在

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

.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)])

【讨论】:

检查这个:the twin injectors【参考方案2】:

好的。看完你的cmets,这是我的答案。

我在 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 引用以下配置和运行块:

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

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

【讨论】:

@Harry Burns 请检查在您的 plunker 中有效的编辑代码 myProvider.providerInjector.get('myServiceProvider') 将只返回服务的提供者包装器,而不是服务本身。而且你没有回答我的第一个问题——为什么有两个 $injector?和有什么区别?以及如何在配置块中获取“service $injector”? 那么你不认为你有两个问题。 1. 为什么有两个不同的 $injector。 2.如何在配置中使用$inject。 ?对于第二个问题,答案是,您不能在配置中使用 $injector,因为在配置块中无法实例化服务。正如我在上面的答案中提到的那样。【参考方案3】:

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

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

【讨论】:

以上是关于AngularJS 两个不同的 $injector的主要内容,如果未能解决你的问题,请参考以下文章

如何解决 angularjs 未捕获的错误:[$injector:modulerr]?

angularJs $injector

AngularJS 1.2 $injector:modulerr

AngularJS 错误:$injector:unpr 未知提供者

错误:[$injector:unpr] 未知提供者:在 AngularJS 服务测试中

AngularJs中Uncaught Error: [$injector:modulerr] http://errors.angularjs.org/1.3.15/