本篇文章继续介绍angular用以实现依赖注入的关键元素之一 - 注解(Annotation)。
在前几篇文章中,我们已经分析和讨论了有关angular依赖注入的几个方面:
既然我们定义的服务和数据都已经被angular注入器托管在其内部的缓存中了,接下来应该如何使用它呢?写过angular应用的同学们应该都写过下面这类代码:
var testApp = angular.module('test', []);
testApp.controller('testController', function($scope, $rootScope) {
// ......
});
我们直接在testContoller
的函数中定义了两个服务,$scope
以及$rootScope
,然后就能够在应用的业务逻辑中使用这两项服务了,不需要自己将它们创建出来,也不需要做什么特别的准备工作,一切都显的水到渠成。但是!程序开发并不是变魔法,看似不可思议的事情背后总是有支撑它发生的逻辑。我们都知道这个逻辑就是依赖注入,而依赖注入的关键在于注入器,所以问题就演变成了注入器是如何找到对应的被托管的对象的呢?
答案是通过注解(Annotation)。所谓注解,它的本质就是给源代码添加一些元数据。有Java开发经验的同学想必都见过@Override
,@Deprecated
以及@SuppressWarnings
这类常用注解吧。它们的意义分别是表明某个方法覆盖了/实现了父类型(可以是父类,也可以是接口)上的同名方法;表明某个方法已经废弃了,不推荐再使用;抑制编译器产生警告信息。因为这类信息十分必要,但是又不好直接以传统意义上的源代码的形式体现出来,所以才设计出了注解这种数据类型。
那么切换到angular的上下文中,又是如何来实现注解的呢?这个注解需要解决什么问题呢?这就是本篇文章需要分析和讨论的主题。
我们已经知道定义的各种服务的函数实际上并不由我们自己来调用,而是交给angular框架提供的注入器进行调用,在调用的过程中首先我们需要知道注入器在哪个阶段会需要使用到注解提供的信息。其实在上一篇文章中在介绍注入器实例化托管对象的过程中可能发生循环依赖异常时就已经有一些线索了,这些线索就隐藏在异常的调用栈之中,我们来看看:
angular.js:13920 Error: [$injector:cdep] Circular dependency found: service1 <- service2 <- service1
http://errors.angularjs.org/1.5.8/$injector/cdep?p0=service1%20%3C-%20service2%20%3C-%20service1
at angular.js:68
at getService (angular.js:4656)
at injectionArgs (angular.js:4688)
at Object.instantiate (angular.js:4730)
at Object.<anonymous> (angular.js:4573)
at Object.invoke (angular.js:4718)
at Object.enforcedReturnValue [as $get] (angular.js:4557)
at Object.invoke (angular.js:4718)
at angular.js:4517
at getService (angular.js:4664)
可以看到在几个调用点: Object.instantiate
-> injectionArgs
-> getService
。
毫无疑问,从字面意思上就能够理解这里发生了什么。首先注入器会尝试实例化一个被托管的对象,在实例化的过程中由于该对象也存在依赖关系,需要首先解析这些依赖关系,得到依赖关系之后,才能够调用getService
完成真正的实例化操作。而解析依赖关系实际上就是我们所关注的注解的生成过程。那么在清楚了注解的应用场景后,让我们看看injectionArgs
这个函数的逻辑是怎样的:
function injectionArgs(fn, locals, serviceName) {
var args = [],
// 获取注解信息<