上一篇文章介绍了Dagger2的基本用法,这篇文章主要说一下Dagger2中@Scope的用法和原理。java
上一篇文章中提到:android
如上面例子所示,若是要求D对象为单例,能够经过@Singleton注解来实现。首先咱们须要在依赖图中声明对象是单例的:git
@Module public class DModule { @Provides @Singleton public D provideD() { return new D(); } }DComponent接口也须要声明:github
@Singleton @Component(modules = DModule.class) public interface DComponent { D provideD(); }如此,当咱们注入D对象时,可保证每次注入的是同一个D对象:app
DComponent dComponent = DaggerDComponent.create(); D d1 = dComponent.provideD(); D d2 = dComponent.provideD(); // d1 == d2
在咱们看来,只是多加了一个注解而已,便实现了单例模式。要知道其原理,要从Dagger2生成的源码入手。ide
以以下例子为例:工具
定义类:ui
public class A { public A(){ } } public class B { A a; public B(A a) { this.a = a; } } public class C { A a; B b; public C(A a, B b) { this.a = a; this.b = b; } }
定义Modulethis
@Module public class ABCModule { @Provides public A provideA() { return new A(); } @Provides public B provideB(A a) { return new B(a); } @Provides public C provideC(A a, B b) { return new C(a, b); } }
定义Component接口:.net
@Component(module=ABCModule.class) public interface ABCComponent { public A provideA(); public B provideB(); public C provideC(); void inject(Main main); }
依赖注入:
public class Main {
@Inject C c; public Main() { ABCComponent abcComponent = DaggerABCComponent .builder() .dComponent(dComponent) .build(); A a = abcComponent.provideA(); B b = abcComponent.provideB(); abcComponent.inject(this); }
}
编译工程,Dagger2在项目路径下生成了以下文件:
[dagger2] ┣━[di] ┃ ┣━[component] ┃ ┃ ┗━DaggerABCComponent.java ┃ ┗━[module] ┃ ┣━ABCModule_ProvideAFactory.java ┃ ┣━ABCModule_ProvideBFactory.java ┃ ┗━ABCModule_ProvideCFactory.java ┗━[model] ┗━Main_MembersInjector.java
(利用这个工具生成了文件结构图)
注意,生成的文件在关联的类相同路径下。如DaggerABCComponent类生成在ABCComponent路径下。
咱们先来看看实际接触到的DaggerABCComponent类:
@Generated("dagger.internal.codegen.ComponentProcessor") public final class DaggerABCComponent implements ABCComponent { private Provider<A> provideAProvider; private Provider<B> provideBProvider; private Provider<C> provideCProvider; private MembersInjector<Main> mainMembersInjector; private DaggerABCComponent(Builder builder) { assert builder != null; initialize(builder); } public static Builder builder() { return new Builder(); } public static ABCComponent create() { return builder().build(); } private void initialize(final Builder builder) { this.provideAProvider = ABCModule_ProvideAFactory.create(builder.aBCModule); this.provideBProvider = ABCModule_ProvideBFactory.create(builder.aBCModule, provideAProvider); this.provideCProvider = ABCModule_ProvideCFactory.create(builder.aBCModule, provideAProvider, provideBProvider); this.mainMembersInjector = Main_MembersInjector.create(provideCProvider); } @Override public A provideA() { return provideAProvider.get(); } @Override public B provideB() { return provideBProvider.get(); } @Override public C provideC() { return provideCProvider.get(); } @Override public void inject(Main main) { mainMembersInjector.injectMembers(main); } public static final class Builder { private ABCModule aBCModule; private Builder() { } public ABCComponent build() { if (aBCModule == null) { this.aBCModule = new ABCModule(); } return new DaggerABCComponent(this); } public Builder aBCModule(ABCModule aBCModule) { if (aBCModule == null) { throw new NullPointerException("aBCModule"); } this.aBCModule = aBCModule; return this; } } }
来看几个关键点:
DaggerABCComponent继承于ABCComponent。因此咱们能够直接调用ABCComponent的方法。
DaggerABCComponent须要Builder来进行初始化。Builder的做用是提供对象的module。
对象经过Provider从依赖图中取出。Provider由Factory生成时会有相似依赖注入的操做。
经过MembersInjector进行依赖注入。
这几个关键类的关系可用下图表示:
+---------------------------------------+ | DaggerABCComponent | | | | +----------+ create +-----------+------------+ | | Factory +-----+-----> | Provider<A> | | +----+-----+ | +----+------+------------+ | ^ | | | | | | | | +--v------+------------+ | | +-----> | |Provider<B> | | | ABCModule | | +--+------+------------+ | | | | | | | | | +----v------+------------+ | +----+----+ +-----> | Provider<C> | | | Builder | +-----------+------------+ | +---------+ | | | | +--------------------+ +-----------+------------+ | |Main_MembersInjector+--> | MembersInjector<Main> | | +--------------------+ +-----------+------------+ | | +---------------------------------------+
其中最最关键的是Factory和Provider。以B类为例,从依赖图中取出B对象,须要通过以下代码:
... this.provideBProvider = ABCModule_ProvideBFactory.create(builder.aBCModule, provideAProvider); ... @Override public B provideB() { return provideBProvider.get(); }
其中ABCModule_ProvideBFactory的源码以下所示:
@Generated("dagger.internal.codegen.ComponentProcessor") public final class ABCModule_ProvideBFactory implements Factory<B> { private final ABCModule module; private final Provider<A> aProvider; public ABCModule_ProvideBFactory(ABCModule module, Provider<A> aProvider) { // 根据以前的依赖关系,注入ProviderA assert module != null; this.module = module; assert aProvider != null; this.aProvider = aProvider; } @Override public B get() { B provided = module.provideB(aProvider.get()); // 从ProviderA中取出A对象,再生成B对象 if (provided == null) { throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); } return provided; } public static Factory<B> create(ABCModule module, Provider<A> aProvider) { return new ABCModule_ProvideBFactory(module, aProvider); } }
Factory和Provider接口以下所示:
public interface Factory<T> extends Provider<T> { } public interface Provider<T> { T get(); }
从使用者的角度看,无需关心对象是如何生成的,只需调用provider的get方法便可得到对象。并且对象应该是符合既定的规则而且初始化好能够立刻用的。
从ABCModule_ProvideBFactory(或者某个Provider)的角度看,在初始化方法里就明确了本身所需依赖的对象(这里是ProviderA)。在get方法的实现里,只需关心B对象的生成。当须要A对象时,直接从外部“注入”的providerA取出便可。
再来看一看Main_MembersInjector的实现:
@Generated("dagger.internal.codegen.ComponentProcessor") public final class Main_MembersInjector implements MembersInjector<Main> { private final Provider<C> cProvider; public Main_MembersInjector(Provider<C> cProvider) { assert cProvider != null; this.cProvider = cProvider; } @Override public void injectMembers(Main instance) { if (instance == null) { throw new NullPointerException("Cannot inject members into a null reference"); } instance.c = cProvider.get(); } public static MembersInjector<Main> create(Provider<C> cProvider) { return new Main_MembersInjector(cProvider); } }
Dagger2在编译时会分析module中inject方法的参数的类型(这里是Main类),记录下用@Inject注解标注的成员,而后生成对应的Injector。
理解Injector的关键在理解它的构造方法和injectMembers方法。instance.c = cProvider.get();
一句实施了依赖注入。
@Singleton
修改ABCModule以下所示:
@Module public class ABCModule { ... @Provides @Singleton // 添加Singleton注解 public B provideB(A a) { return new B(a); } ... }
修改ABCComponent以下所示:
@Singleton @Component(module=ABCModule.class) public interface ABCComponent { ... }
咱们来看看Dagger2生成的代码有什么不一样:
@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerABCComponent implements ABCComponent {
...
private Provider provideBProvider;
...
private void initialize(final Builder builder) {
... this.provideBProvider = ScopedProvider.create(ABCModule_ProvideBFactory.create(builder.aBCModule, provideAProvider)); ...
}
...
@Override
public B provideB() {
return provideBProvider.get();
}
...
}
能够看到惟一的不一样是用ScopedProvider将ABCModule_ProvideBFactory包裹起来。来看一下ScopedProvider的源码:
package dagger.internal; import javax.inject.Provider; public final class ScopedProvider<T> implements Provider<T> { private static final Object UNINITIALIZED = new Object(); private final Factory<T> factory; private volatile Object instance = UNINITIALIZED; private ScopedProvider(Factory<T> factory) { assert factory != null; this.factory = factory; } @SuppressWarnings("unchecked") // cast only happens when result comes from the factory @Override public T get() { // double-check idiom from EJ2: Item 71 Object result = instance; if (result == UNINITIALIZED) { synchronized (this) { result = instance; if (result == UNINITIALIZED) { instance = result = factory.get(); } } } return (T) result; } /** Returns a new scoped provider for the given factory. */ public static <T> Provider<T> create(Factory<T> factory) { if (factory == null) { throw new NullPointerException(); } return new ScopedProvider<T>(factory); } }
理解上面的代码关键在于:
ScopedProvider在dagger.internal下,非Dagger2自动生成。
ScopedProvider也是一个Provider
利用double-check,在instance上实现了单例模式。也就是说,在ScopedProvider的生命周期内,get返回的都是同一个对象。
以上3点实现了无入侵式的Singleton模式。但其实ScopedProvider并非专为Singleton模式设计的,Singleton模式只是Dagger2中Scope功能的效果。
@Singleton注解的源码以下所示:
@Scope @Documented @Retention(RUNTIME) public @interface Singleton {} ... @Target(ANNOTATION_TYPE) @Retention(RUNTIME) @Documented public @interface Scope {}
能够看出,@Singleton只是一个标记,代表这是一个Scope。那么Scope是什么呢?源代码中有如此注释:
A scope annotation applies to a class
containing an injectable constructor and governs how the injector reuses
instances of the type. By default, if no scope annotation is present, the
injector creates an instance (by injecting the type's constructor), uses
the instance for one injection, and then forgets it. If a scope annotation
is present, the injector may retain the instance for possible reuse in a
later injection.
简单地说,@Scope决定了注射器从依赖图中取出对象的行为。若是节点有Scope标签,那么注入时将重用上次生成的对象。
依赖图中某个节点标注了@Scope后,便拥有了与当前Component相同的生命周期。也就是说,若是要实现全局(Application范围内)的Singleton,必须要有全局的Component。这就是为何许多其余关于Dagger2的例子中,要在Application中保持ApplicationComponent的引用的缘由。
至于在许多例子中看到的@PerActivity(对象在当前Activity的生命周期内惟一),@PerUser(对象在当前用户态销毁前惟一),它们的实现也是依赖于Component的生命周期。因此须要在Activity的onCreate中新建SomeActivityComponent并保持引用,在UserManager的login中新建UserComponent并保持引用。