开源项目源码解析-Dagger 源码解析
Dagger 源码解析
1. 功能介绍1.1 DaggerDagger 是一款 Java 平台的依赖注入库,关于依赖注入,详细见依赖注入简介。 Java 的依赖注入库中,最有名的应该属 Google 的 Guice,Spring 也很有名,不过是专注于 J2EE 开发。Guice 的功能非常强大,但它是通过在运行时读取注解来实现依赖注入的,依赖的生成和注入需要依靠 Java 的反射机制,这对于对性能非常敏感的 Android 来说是一个硬伤。基于此,Dagger 应运而生。 Dagger 同样使用注解来实现依赖注入,但它利用 APT(Annotation Process Tool) 在编译时生成辅助类,这些类继承特定父类或实现特定接口,程序在运行时 Dagger 加载这些辅助类,调用相应接口完成依赖生成和注入。Dagger 对于程序的性能影响非常小,因此更加适用于 Android 应用的开发。 1.2 依赖注入相关概念依赖(Dependency):如果在 Class A 中,有个属性是 Class B 的实例,则称 Class B 是 Class A 的依赖,本文中我们将 Class A 称为宿主(Host),并且全文用 Host 表示;Class B 称为依赖(Dependency),并且全文用 Dependency 表示。一个 Host 可能是另外一个类的 Dependency。 宿主(Host):如果 Class B 是 Class A 的 Dependency,则称 Class A 是 Class B 的宿主(Host)。 依赖注入:如果 Class B 是 Class A 的 Dependency,B 的赋值不是写死在了类或构造函数中,而是通过构造函数或其他函数的参数传入,这种赋值方式我们称之为依赖注入。 更详细介绍可见依赖注入简介。 1.3 Dagger 基本使用本文将以一个简单的“老板和程序员” App 为例。 Activity 中有一个 Boss 类属性,现在你想把一个 Boss 对象注入到这个 Activity 中,那么有两个问题需要解决:Boss 对象应该怎样被生成 以及 Boss 对象怎样被设置到 Activity 中。 (1). Boss 对象怎样生成在 Boss 类的构造函数前添加一个 @Inject 注解,Dagger 就会在需要获取 Boss 对象时,调用这个被标记的构造函数,从而生成一个 Boss 对象。 public class Boss { ... @Inject public Boss() { ... } ... } 需要注意的是,如果构造函数含有参数,Dagger 会在调用构造对象的时候先去获取这些参数(不然谁来传参?),所以你要保证它的参数也提供可被 Dagger 调用到的生成函数。Dagger 可调用的对象生成方式有两种:一种是用 @Inject 修饰的构造函数,上面就是这种方式。另外一种是用 @Provides 修饰的函数,下面会讲到。 (2). Boss 对象怎样被设置到 Activity 中通过 @Inject 注解了构造函数之后,在 Activity 中的 Boss 属性声明之前也添加 @Inject 注解。像这种在属性前添加的 @Inject 注解的目的是告诉 Dagger 哪些属性需要被注入。 public class MainActivity extends Activity { @Inject Boss boss; ... } 最后,我们在合适的位置(例如 onCreate() 函数中)调用 ObjectGraph.inject() 函数,Dagger 就会自动调用上面 (1) 中的生成方法生成依赖的实例,并注入到当前对象(MainActivity)。 public class MainActivity extends Activity { @Inject Boss boss; @Override protected void onCreate(Bundle savedInstanceState) { ObjectGraph.create(AppModule.class).inject(this); } ... } 具体怎么注入即设置的过程后面会详细介绍,这里简单透露下,APT 会在 MainActivity 所在 package 下生成一个辅助类 MainActivity$$InjectAdapter,这个类有个 injectMembers() 函数,代码类似: public void injectMembers(MainActivity paramMainActivity) { paramMainActivity.boss = ((Boss)boss.get()); …… } 上面我们已经通过 ObjectGraph.inject() 函数传入了 paramMainActivity,并且 boss 属性是 package 权限,所以 Dagger 只需要调用这个辅助类的 injectMembers() 函数即可完成依赖注入,这里的 boss.get() 会调用 Boss 的生成函数。 (3). ObjectGraph.create(AppModule.class) 函数简介上面 onCreate() 函数中出现了两个类:ObjectGraph 和 AppModule。其中 ObjectGraph 是由 Dagger 提供的类,可以简单理解为一个依赖管理类,它的 create() 函数的参数是一个数组,为所有需要用到的 Module(例如本例中的 AppModule)。AppModule 是一个自定义类,在 Dagger 中称为 @Module(injects = MainActivity.class) public class AppModule { } 可以看到,AppModule 是一个空类,除了一行注解外没有任何代码。 1.4 自定义依赖生成方式(1). @Provides 修饰的生成函数对构造函数进行注解是很好用的依赖对象生成方式,然而它并不适用于所有情况。例如:
对于以上三种情况,可以使用 @Provides 注解来标记自定义的生成函数,从而被 Dagger 调用。形式如下: @Provides Coder provideCoder(Boss boss) { return new Coder(boss); } 和构造函数一样,@Provides 注解修饰的函数如果含有参数,它的所有参数也需要提供可被 Dagger 调用到的生成函数。 @Module public class AppModule { @Provides Coder provideCoder(Boss boss) { return new Coder(boss); } } (2). @Inject 和 @Provide 两种依赖生成方式区别a. @Inject 用于注入可实例化的类,@Provides 可用于注入所有类 1.5 单例Dagger 支持单例(事实上单例也是依赖注入最常用的场景),使用方式也很简单: // @Inject 注解构造函数的单例模式 @Singleton public class Boss { ... @Inject public Boss() { ... } ... } // @Provides 注解函数的单例模式 @Provides @Singleton Coder provideCoder(Boss boss) { return new Coder(boss); } 在相应函数添加 @Singleton 注解,依赖的对象就只会被初始化一次,之后的每次都会被直接注入相同的对象。 1.6 Qualifier(限定符)如果有两类程序员,他们的能力值 power 分别是 5 和 1000,应该怎样让 Dagger 对他们做出区分呢?使用 @Qualifier 注解即可。 (1). 创建一个 @Qualifier 注解,用于区分两类程序员: @Qualifier @Documented @Retention(RUNTIME) public @interface Level { String value() default ""; } (2). 为这两类程序员分别设置 @Provides 函数,并使用 @Qualifier 注解对他们做出不同的标记: @Provides @Level("low") Coder provideLowLevelCoder() { Coder coder = new Coder(); coder.setName("战五渣"); coder.setPower(5); return coder; } @Provides @Level("high") Coder provideHighLevelCoder() { Coder coder = new Coder(); coder.setName("大神"); coder.setPower(1000); return coder; } (3). 在声明 @Inject 对象的时候,加上对应的 @Qualifier 注解。 @Inject @Level("low") Coder lowLevelCoder; @Inject @Level("high") Coder highLevelCoder; 1.7 编译时检查实质上,Dagger 会在编译时对代码进行检查,并在检查不通过的时候报编译错误,具体原因请看下面的详细原理介绍。检查内容主要有三点: 1.8 Dagger 相关概念Module:也叫 ModuleClass,指被 @Module 注解修饰的类,为 Dagger 提供需要依赖注入的 Host 信息及一些 Dependency 的生成方式。 ModuleAdapter:指由 APT 根据 @Module 注解自动生成的类,父类是 Dagger 的 ModuleAdapter.java,与 ModuleClass 对应,以 ModuleClass 的 ClassName 加上 $$ModuleAdapter 命名,在 ModuleClass 的同一个 package 下。 Binding:指由 APT 根据 @Inject 注解和 @Provides 注解自动生成,最终继承自 Binding.java 的类。为下面介绍的 DAG 图中的一个节点,每个 Host 及依赖都是一个 Binding。 InjectAdapter:每个属性或构造函数被 @Inject 修饰的类都会生成一个 继承自 Binding.java 的子类,生成类以修饰类的 ClassName 加上 $$InjectAdapter 命名,在该类的同一个 package 下。 ProvidesAdapter:每个被 @Provides 修饰的生成函数都会生成一个继承自 ProvidesBinding.java 的子类,ProvidesBinding.java 继承自 Binding.java,生成类以 Provide 函数名首字母大写加上 ProvidesAdapter 命名,是 Provide 函数所在 Module 对应生成的 Binding 安装:指将 Binding 添加到 Binding 库中。对 Dagger Linker.java 代码来说是将 Binding 添加到 Linker.bindings 属性中,Linker.bindings 属性表示某个 ObjectGraph 已安装的所有 Binding。对于下面的 DAG 图来说是将节点放到图中,但尚未跟其他任何节点连接起来。 Binding 连接:把当前 Binding 和它内部依赖的 Binding 进行连接,即初始化这个 Binding 内部的所有 Binding,使它们可用。对 DAG 的角度说,就是把某个节点与其所依赖的各个节点连接起来。 2. 总体设计2.1 概述事实上,Dagger 这个库的取名不仅仅来自它的本意“匕首”,同时也暗示了它的原理。Jake Wharton 在对 Dagger 的介绍中指出,Dagger 即 DAG-er,这里的 DAG 即数据结构中的 DAG——有向无环图(Directed Acyclic Graph)。也就是说,Dagger 是一个基于有向无环图结构的依赖注入库。 2.2 DAG(有向无环图)已经了解 DAG 的可以跳过这节。 上图中的数据结构就是一个有向无环图。图中一共存在 6 个节点和 7 个箭头,但任何一个节点都无法从自己发射出的箭头通过某条回路重新指向自己。 2.3 Dagger 中依赖注入与 DAG 的关系Dagger 的运作机制,是运用APT(Annotation Process Tool)在编译时生成一些用于设定规则的代码,然后在运行时将这些规则进行动态组合 // TODO 不太理解意思,生成一个(或多个)DAG,然后由 DAG 来完成所有依赖的获取,实现依赖注入。关于 DAG 究竟是怎样一步步生成的,后面再讲,这里先说一下在 Dagger 中依赖注入与 DAG 的关系。 我把前面那张图的每个节点重新命名,得到了上图。上图代表了某个应用程序内的一整套依赖关系,其中每个箭头都表示两个类之间依赖关系,Host 和 Dependency 都是其中的一个节点。 可以看出,一个程序中的整套依赖关系其实就是一个 DAG。而实际上,Dagger 也是这么做的:预先建立一个 DAG,然后在需要获取对象的时候通过这个依赖关系图来获取到对象并返回,若获取失败则进行查找,查找到后再补充到 DAG 中。 Dagger 是支持传递依赖的。例如在上图中,当需要获取一个 CustomView,会首先获取一个 DataHelper 作为获取 CustomView 的必要参数;此时如果 DataHelper 还未初始化,则还要分别拿到 HttpHelper 和 Database 用来初始化 DataHelper;以此类推。 Dagger 不支持循环依赖,即依赖关系图中不能出现环。原因很简单,如果鸡依赖蛋,蛋依赖鸡,谁来创造世界?总有一个要先产生的。 2.4 工作流程(1). 编译时,通过 APT 查看所有 java 文件,并根据注解生成一些新的 java 文件,即 3. 流程图3.1 编译时:3.2 运行时(初始化后):4. 详细设计4.1 类关系图上图是 Dagger 整体框架最简类关系图。大致原理可以描述为: 4.2 类功能详细介绍4.2.1 Binding.java —— 节点Binding 是一个泛型抽象类,相当于依赖关系 DAG 图中的节点,依赖关系 DAG 图中得每一个节点都有一个由 APT 生成的继承自 Binding 的类与之对应,而依赖关系 DAG 图中的每一个节点与 (1). Binding.java 实现的接口Binding.java 实现了两个接口,第一个是 javax 的 (2). 生成的 Binding 代码示例如下的 Host 和 Dependency 类 public class Host { @Inject Dependency dependency; } public class Dependency { @Inject public Dependency() { …… } } 由 APT 生成的 Binding 应该类似 public final class Host$$InjectAdapter extends Binding<Host> implements MembersInjector<Host> { private Binding<Dependency> dependencyBinding; …… public void attach(Linker linker) { dependencyBinding = (Dependency$$InjectAdapter)linker.requestBinding(……); } public void injectMembers(Host host) { host.dependency = (Dependency)dependencyBinding.get(); } } public final class Dependency$$InjectAdapter extends Binding<Dependency> implements Provider<Dependency> { …… public Dependency get() { return new Dependency(); } }
(3). Binding 分类上面我们将生成的 Binding 子类简单分为了 对于 @Inject 方式的注入,APT 会在 所以实际自动生成的 Binding 子类我们可以分为三种: 第二种是 Inject Dependecy 对应的 Binding 子类,本文中我们统一称为 第三种是 Provide Dependecy 对应的 Binding 子类,本文中我们统一称为 Binding.java 的主要函数: (1). get()表示得到此 Binding 对应的 (2). injectMembers(T t)表示向此 Binding 对应 (3). attach(Linker linker)表示 (4). getDependencies(…)表示 Binding.java 的主要属性: (1). provideKey表示 Binding 所属 Host 或 Dependency 的类名,是 Binding 唯一的 key,在 Linker 管理 Binding 时会用到,作为存储所有 Binding 的 Map 的 key。对 (2). membersKey// TODO。对 (3). requiredBy表示这个 Binding 属于谁,对 (4). bits表示 Binding 特性的标志位,如是是否是单例(SINGLETON)、是否已连接(LINKED),是否被访问(VISITING)、是否是可被其他 Module 依赖的 Library(LIBRARY)、是否依赖其他 Module 的 Binding(DEPENDED_ON)、是否不存在循环依赖(CYCLE_FREE)。 4.2.2 Linker.java —— 拼装者Linker 是 Dagger 最核心的大脑部分,它负责调用 Loader 加载 Binding,存储并管理所有 Binding、调用 attach 方法初始化依赖的 DependencyBinding。对于 DAG 图来说,Linker 就相当于一个管家,负责调用加载器加载节点到图中、存储并管理图中所有的节点,连接图中有依赖关系的节点,也就是 DAG 图的拼装。 Linker.java 的主要属性: (1). bindings本文称为 ObjectGraph 的 Binding 库,表示 ObjectGraph 已安装的所有 Binding,包括尚未连接的 Binding,对于 DAG 图来说就是所有在图中的节点,包括尚未跟其他任何节点连接起来的节点。 (2). toLink表示待连接的 Binding 队列,包含了所有待连接的 Binding。对于 DAG 图来说就是所有在图中但未和任何节点连接的节点。 连接(Link):从 DAG 的角度说,就是把某个节点与其所依赖的各个节点连接起来。而对于 Binding 来说,就是把当前 Binding 和它依赖的 Binding ( (3). attachSuccess一个标志,对于某个 Binding,在获取它依赖的 (4). linkedBindings默认为 null,只有在 linkAll() 函数被调用后才有效,用于存储所有已经连接的 Binding,同时也是一个标记,表示这个 ObjectGraph 已经不能被改变。 (5). Loader pluginLoader 负责加载类,主要是加载 APT 生成的辅助类(InjectAdapter、ModuleAdapter)。 (6). errorsLinker.linkRequested() 运行过程中积累的 errors。 Linker.java 的主要函数: (1). requestBinding(String key ……)根据传入的 key 返回一个 Binding。首先,会尝试从 Bindings 变量(Binding 库)中查找这个 key,如果找到了,就将找到的 Binding 返回(如果找到后发现这个 Binding 还未连接,还需要它放进 toLink 中);如果找不到,说明需要的 Binding 是一个 (2). linkRequested()循环取出 toLink 中的 Binding: (3). installBindings(BindingsGroup toInstall)安装 Bindings,表示将 Binding 添加到 ObjectGraph 中,但尚未连接。对 DAG 图来说就是就是将节点放到图中,但尚未和任何其他节点连接。 (4). linkAll()将 Binding 库中所有未连接的 Binding 添加到 toLink 中,调用 linkRequested() 进行连接。 (5). fullyLinkedBindings()返回已经全部连接的 Binding,如果没有调用过 linkAll() 则返回 null 4.2.3 Loader.java —— 类加载器及对象生成器Loader 是一个纯工具类,它通过 ClassLoader 加载 APT 生成的 Loader.java 的主要函数: (1). loadClass(ClassLoader classLoader,String name)用指定的 ClassLoader 根据类名得到类,并缓存起来。 (2). instantiate(String name,ClassLoader classLoader)用指定的 ClassLoader 根据类名获取类的实例。 (3). getModuleAdapter(Class moduleClass)获取指定的 Module 类所对应的 ModuleAdapter 实例。 (4). getAtInjectBinding(String key……)根据 key 获取 Inject Dependecy 对应的 InjectAdapter 实例。 (5). getStaticInjection(Class<?> injectedClass)根据被注入的 Class 获取对应的 StaticInjection 实例。 Loader.java 的主要变量: (1). Memoizer>> caches用来缓存被初始化过的对象,是一个嵌套的 Memoizer 结构, 4.2.4 FailoverLoader.javaFailoverLoader 是 Loader 的一个子类,它加载类的策略是首先查找 APT 生成的类,如果查找失败,则直接使用反射查找和初始化。 (1). getModuleAdapter(Class moduleClass)获取指定的 Module 类所对应的 ModuleAdapter 实例,如果在生成类中查找失败,则会调用 ReflectiveAtInjectBinding.create(type,mustHaveInjections) 通过反射直接初始化对象。 (2). getAtInjectBinding(String key……)根据 key 获取 Inject Dependecy 对应的 InjectAdapter 实例。如果在生成类中查找失败,则会调用 ReflectiveStaticInjection.create(injectedClass) 通过反射直接初始化对象。 (3). getStaticInjection(Class<?> injectedClass)FailoverLoader.java 的主要变量: (1). Memoizer,ModuleAdapter<?>> loadedAdapters用来缓存初始化过的 ModuleAdapter 对象,是一个嵌套的 Memoizer 结构,具体可看下面介绍,简单理解就是嵌套的 HashMap,第一层 Key 是 ClassLoader,第二层 Key 是 ClassName,Value 是 Class 对象。 4.2.5 ObjectGraph —— 管理者ObjectGraph 是个抽象类,负责 Dagger 所有的业务逻辑,Dagger 最关键流程都是从这个类发起的,包括依赖关系图创建、实例(依赖或宿主)获取、依赖注入。 (1). create(Object... modules)这是个静态的构造函数,用于返回一个 ObjectGraph 的实例,是使用 Dagger 调用的第一个函数。参数为 ModuleClass 对象,函数作用是根据 ModuleClass 构建一个依赖关系图。此函数实现会直接调用 DaggerObjectGraph.makeGraph(null,new FailoverLoader(),modules) 返回一个 (2). inject(T instance)抽象函数,表示向某个 Host 对象中注入依赖。 (3). injectStatics()抽象函数,表示向 ObjectGraph 中相关的 Host 注入静态属性。 (4). get(Class type)抽象函数,表示得到某个对象的实例,多用于得到依赖的实例。 (5). plus(Object... modules)抽象函数,表示返回一个新的包含当前 ObjectGraph 中所有 Binding 的 ObjectGraph。 (6). validate()抽象函数,表示对当前 ObjectGraph 做检查。 4.2.6 DaggerObjectGraphDaggerObjectGraph 是 ObjectGraph 的静态内部类,也是 ObjectGraph 目前唯一的子类。因为 ObjectGraph 的 create() 函数直接返回了 DaggerObjectGraph 对象,所以对 Dagger 的调用实际都是对 DaggerObjectGraph 的调用。 (1). Map injectableTypes记录了所有需要被依赖注入的 Host 类型,以 Host 的 ClassName 加上一定规则前缀(// TODO)做为 key,以其所对应的 Module 为 value。 (2). Map staticInjections记录了所有需要被静态依赖注入的 Host 类型,以 Host 的 ClassName 加上一定规则前缀(// TODO)做为 key,以其所对应的 Module 为 value。 (3). Linker linkerLinker 是 负责调用 Loader 加载 Binding,存储并管理所有 Binding、调用 attach 方法初始化依赖的 DependencyBinding。具体见上面 (4). Loader pluginLoader 负责通过 ClassLoader 加载 APT 生成的ModuleAdapter类和InjectAdapter类。 (1). makeGraph 函数makeGraph 函数首先会通过 Modules.loadModules 函数得到所有的 ModuleAdapter; 表示向某个 Host 对象中注入依赖。首先根据下面的 getInjectableTypeBinding() 函数查找到 Host 对应的 InjectBinding,然后调用 injectMembers() 函数注入依赖,将依赖注入结束的 Host 返回。
表示得到某个对象的实例,多用于得到 Denpendency 的实例。首先根据下面的 getInjectableTypeBinding() 函数查找到 Denpendency 对应的 Binding,然后调用 get() 返回该 Denpendency 实例。
表示对当前 ObjectGraph 做检查,首先会利用 Linker 查找到所有节点并连接起来,然后调用 ProblemDetector 进行检查。ProblemDetector 会在后面解释作用。
|