Component注解

Component注解用于注解一个接口或抽象类,以便从其Module集合中生成一个完全成型的依赖注入实现。

每个使用@Component注解的类型必须包含至少一个抽象组件方法(Component methods),组件方法可以拥有任何名称,但其签名必须遵循供给和成员注入协约。

供给方法(Provision methods)

供给方法没有参数,并返回一个被注入或者被供给的类型,每个方法也可以有一个限定符注解,以下都是有效的供给方法:

SomeType getSomeType();
Set<SomeType> getSomeTypes();
@PortNumber int getPortNumber();

供给方法和注入点一样可以使用ProviderLazy更加显示地控制供给请求,Provider允许组件用户通过调用Provider.get()来请求任意次的供给,Lazy自始至终只会请求单个供给,但会将其推迟到对Lazy.get()的首次调用。以下供给方法都请求对同一类型的供给,但分别应用了不同的机制:

SomeType getSomeType();
Provider<SomeType> getSomeTypeProvider();
Lazy<SomeType> getLazySomeType();

成员注入方法(Members-injection methods)

成员注入方法具有单个参数,并将依赖注入到传入实例的每个使用Inject注解的域和方法,一个成员注入方法可以是void的,也可以返回其单个参数以便链接。以下都是合法的成员注入方法声明:

void injectSomeType(SomeType someType);
SomeType injectAndReturnSomeType(SomeType someType)

一个没有参数但返回MembersInjector的方法和成员注入方法是等效的,在返回对象上调用MembersInjector.injectMembers(T)方法和成员注入方法的作用一样:

MembersInjector<SomeType> getSomeTypeMembersInjector();

关于协方差的注意事项

尽管一个类型的成员注入方法会可以接收其子类的实例(参数是返回类型的子类),只有参数类型和其超类的Inject注解成员会被注入,而参数类型的子类则不会,例如,如下类型中,当将Child实例作为参数传入成员注入方法injectSelf(Self instance)时只有a和b会被注入,

class Parent {
 @Inject A a;
}

class Self extends Parent {
 @Inject B b;
}

class Child extends Self {
 @Inject C c;
}

实例化

组件(Component)的实例化主要通过生成的builder。一个builder实例通过组件上的builder()方法获取,返回的builder有一个方法用于设置每个modules() 和组件dependencies(),它们都以每个module或dependency类型的小写驼峰形式命名。每个缺少默认构造函数的组件dependency和module都必须显式地设置,但任何具有默认或无参构造函数(可以被组件实现访问)的module则可以忽略。如下所示:

public static void main(String[] args) {
     OtherComponent otherComponent = ...;
     MyComponent component = DaggerMyComponent.builder()
         // required because component dependencies must be set
         .otherComponent(otherComponent)
         // required because FlagsModule has constructor parameters
         .flagsModule(new FlagsModule(args))
         // may be elided because a no-args constructor is visible
         .myApplicationModule(new MyApplicationModule())
         .build();
}

一种情形中,组件没有依赖组件,并且仅有无参module,那么生成的builder会有一个工厂方法create(),此时SomeComponent.create() 和 SomeComponent.builder().build() 都是有效并等效的。

范围(Scope)

每个Dagger组件都可以使用范围注解来赋予其一个范围,组件实现确保每个组件的实例的每个范围绑定只有一个供给,如果组件申明了一个范围,那么它只能包含无范围的绑定或图中任意处属于该范围的绑定,例如:

   @Singleton @Component
   interface MyApplicationComponent {
     // this component can only inject types using unscoped or @Singleton bindings
   }

为了获取与一个范围注解相关的适当的行为,它的调用者有责任在适当的时候实例化新的组件,例如,一个Singleton组件在一个应用中只应当实例化一次,而一个RequestScoped组件则应当在每次请求是初始化,因为组件是自包含的实现,跳出一个范围就只是简单地丢弃所有对组件的引用。

组件关系(Component relationships)

尽管具有单纯无范围绑定的的独立组件十分有用,许多应用还是会调用许多不同范围的不同组件进行交互,Dagger提供了关联组件的两种机制。

子组件(Subcomponents)

关联两个组件最简单的方式是声明一个子组件,子组件的行为和普通组件极为相似,只不过其实现在一个父组件或子组件内部生成。这一关系允许子组件实现在声明时继承父组件的的全部绑定图,为此,子组件直到关联到一个父组件时才验证其完整性。

子组件通过父组件或子组件上的工厂方法声明,方法可以任意命名,但必须返回子组件,工厂方法的参数可以是任意数量的子组件模块,但必须至少包含没有可见无参构造函数的。下面的例子中,一个工厂方法从一个singleton范围的父组件中创建了一个request范围的子组件。

   @Singleton @Component
   interface ApplicationComponent {
     // component methods...

     RequestComponent newRequestComponent(RequestModule requestModule);
   }

组件依赖(Component dependencies)

尽管子组件是组合绑定的子图(subgraphs of bindings)的最简单的方式,子组件和父组件是强耦合的,他们或许会使用由前辈组件或子组件中定义的任意的绑定,作为一种可选的方法,通过定义一个组件依赖,一个组件只能使用另一个组件的绑定,当一个类型作为组件依赖时,依赖中的每个供给方法作为一个provider进行绑定。注意只有以供给方法暴露的的绑定才可以通过组件依赖获取到。