Dagger 2

网上看了很多这方面的博客,对它的概念也有了大体上的了解,感觉大部分的文章把Dagger的使用写的太乱了,于是打算参考下官网,一下是一些记录:

Why Dagger 2 is Different

各种不同类型的DI框架已经存在了好多年,为什么要重写造轮子呢,官网上是这样说的:Dagger 2 is the first to implement the full stack with generated code。这句话看不懂没关系,下面才是重点:The guiding principle is to generate code that mimics the code that a user might have hand-written to ensure that dependency injection is as simple, traceable and performant as it can be.这句话是说,创作它的指导原则,模仿生成用户需要手动写的一些代码,同时保证简单,可跟踪,尽量减少对性能的影响。

Using Dagger

We’ll demonstrate dependency injection and Dagger by building a coffee maker. For complete sample code that you can compile and run, see Dagger’s coffee example.

这里是说,下面将以制作咖啡这个例子来说明Dagger的使用。

Declaring Dependencies(申明依赖,有点晕?没关系,看完后面你就明白了)

It uses the javax.inject.Inject annotation to identify which constructors and fields it is interested in.

这里是对上面的解释,意思是说,所谓的申明依赖,即是,使用@Inject来表明感兴趣的构造方法和field(对象引用),如果还是不明白,没关系,接着看下面的例子。

Use @Inject to annotate the constructor that Dagger should use to create instances of a class.
(这句话是对例子的一个解释:使用 @Inject来注解构造方法,这样Dagger就可以使用它来创建这个类的对象了)

class Thermosiphon implements Pump {
  private final Heater heater;

  @Inject
  Thermosiphon(Heater heater) {
    this.heater = heater;
  }

  ...
}

Dagger can inject fields directly. In this example it obtains a Heater instance for the heater field and a Pump instance for the pump field.(这句话的你可以理解为,Dagger可以直接在Field(对象引用)上注解,下面的例子中,直接在Heater和Pump两个Field上使用注解)

class CoffeeMaker {
  @Inject Heater heater;
  @Inject Pump pump;

  ...
}

If your class has @Inject-annotated fields but no @Inject-annotated constructor, Dagger will inject those fields if requested, but will not create new instances. Add a no-argument constructor with the @Inject annotation to indicate that Dagger may create instances as well.

上面的主要是说:如果你只是在持有的Field(对应引用)上使用注解,Dagger会在需要的时候把它注入进来,但是并不创建实例。为了让Dagger可以创建这个对象的实例,需要在对象类的无参构造方法上使用@Inject注解。

Dagger可以注解方法和field,常用来注解 构造方法和field

Classes that lack @Inject annotations cannot be constructed by Dagger.(如果类上缺少 @Inject,Dagger就不能创建这个类的对象)

上面啰嗦了一大堆,关键信息:使用 @Inject 来标记我们希望Dagger帮我们自动创建的对象引用,同时在这些对象类的构造方法上添加 @Inject 注解
Satisfying Dependencies(标题看上去有点懵?没关系,看完下面就明白了)

By default, Dagger satisfies each dependency by constructing an instance of the requested type as described above. When you request a CoffeeMaker, it’ll obtain one by calling new CoffeeMaker() and setting its injectable fields.

这句话大概意思是:默认情况下,Dagger通过创建上面描述的类的实例来满足依赖。当你需要一个咖啡机时,Dagger创建一个咖啡机对象给你,同时注入需要的Field(对应引用)

But @Inject doesn’t work everywhere:

这句是重点,这里告诉我们, @Inject并不是在所以情况下都起作用的。以下列出了三种特殊场景:

  1. Interfaces can’t be constructed.
    (接口不能被实例化)
  2. Third-party classes can’t be annotated.(三方类不能被注解)
  3. Configurable objects must be configured!(这个我也不太明白)

For these cases where @Inject is insufficient or awkward, use an @Provides-annotated method to satisfy a dependency. The method’s return type defines which dependency it satisfies.
这句话是说,对于 @Inject 不能满足的这些尴尬情况,可以使用@Provides注解方法来满足依赖。方法的返回类型决定了他满足的了对哪个类的依赖

@Provides static Heater provideHeater() {
  return new ElectricHeater();
}


@Provides static Pump providePump(Thermosiphon pump) {
  return pump;
}



All @Provides methods must belong to a module. These are just classes that have an @Module annotation.
这句话是说,所有的 @Provides方法都必须附属于一个module.所谓的module就是使用 @Module注解的类。

@Module
class DripCoffeeModule {
  @Provides static Heater provideHeater() {
    return new ElectricHeater();
  }

  @Provides static Pump providePump(Thermosiphon pump) {
    return pump;
  }
}

By convention, @Provides methods are named with a provide prefix and module classes are named with a Module suffix.
这句话是给我们提供了一些好的建议:@Provides注解的方法的名字都以provide开头;所有module类的名字都以Module结尾

上面的一大段内容解释了@Provides和@Module注解的由来,关键知识:接口和三方类的注入需要借助@Provides,Provides方法又需要放到一个module中。
Building the Graph

The @Inject and @Provides-annotated classes form a graph of objects, linked by their dependencies. Calling code like an application’s main method or an Android Application accesses that graph via a well-defined set of roots. In Dagger 2, that set is defined by an interface with methods that have no arguments and return the desired type. By applying the @Component annotation to such an interface and passing the module types to the modules parameter, Dagger 2 then fully generates an implementation of that contract.

上面这段英文大概意思是:@Inject 和
@Provides 的作用下,建立起来的依赖关系,组成一张图。application的main方法或Android Application通过一个已经定义好的根的集合访问这张图。在Dagger 2中,这个集合通过在一个接口定义,这个接口中有一个返回类型是需要类型的没有参数的方法。通过在接口上使用@Component的注解,并且向modules参数指定module的类型(可以是复数),Dagger2就可以很好的生成那个协议(接口)的实现类代码。

@Component(modules = DripCoffeeModule.class)
interface CoffeeShop {
  CoffeeMaker maker();
}

The implementation has the same name as the interface prefixed with Dagger. Obtain an instance by invoking the builder() method on that implementation and use the returned builder to set dependencies and build() a new instance.

生成的实现类的名字以Dagger开头,后面跟着接口的名字。通过实现类的builder()方法创建builder对象,并通过builder对象添加各种依赖,最后通过builder的build()创建一个实现类的对象。

CoffeeShop coffeeShop = DaggerCoffeeShop.builder()
    .dripCoffeeModule(new DripCoffeeModule())
    .build();

Note: If your @Component is not a top-level type, the generated component’s name will include its enclosing types’ names, joined with an underscore. For example, this code:

需要注意的是:如果@Component注解的并不是顶级类,Dagger帮我们自动生成的类的名字将会包含包裹层的名字通过下划线连接,下面是一个例子

class Foo {
  static class Bar {
    @Component
    interface BazComponent {}
  }
}

would generate a component named DaggerFoo_Bar_BazComponent.

Any module with an accessible default constructor can be elided as the builder will construct an instance automatically if none is set. And for any module whose @Provides methods are all static, the implementation doesn’t need an instance at all. If all dependencies can be constructed without the user creating a dependency instance, then the generated implementation will also have a create() method that can be used to get a new instance without having to deal with the builder.

拥有默认构造方法的module应该被避免,否则builder将会创建一个实例对象。如果一个module中所有的@Provides注解的方法都是静态的,那么根本不需要一个实现类对象。如果所有的依赖都不需要用户提供一个实例对象,那么生成的实现类中将有一个create()方法,通过这个方法我们可以直接获取一个实现类的对象,而不需要通过builder。

CoffeeShop coffeeShop = DaggerCoffeeShop.create();
Bindings in the graph

The example above shows how to construct a component with some of the more typical bindings, but there are a variety of mechanisms for contributing bindings to the graph. The following are available as dependencies and may be used to generate a well-formed component

这句话我是这样理解的:上面的例子向我们展示了如果使用一些基础的经典bindings来构造一个component。还有各种各样的机制可以改进整个图结构。下面是一些可以使用的依赖,也许可以生成格式更加良好的组件。

Singletons and Scoped Bindings

Annotate an @Provides method or injectable class with @Singleton. The graph will use a single instance of the value for all of its clients.

使用@Singleton注解@Provides注解的方法或者类。对于所有的客户端,整个图将只提供一个该类的实例。

@Provides @Singleton static Heater provideHeater() {
  return new ElectricHeater();
}

The @Singleton annotation on an injectable class also serves as documentation. It reminds potential maintainers that this class may be shared by multiple threads.

使用@Singleton 注解一个类,这个类将同时作为documentation提供服务。它提示潜在的维持着,这个类将可以被多个线程共享。

@Singleton
class CoffeeMaker {
  ...
}

Since Dagger 2 associates scoped instances in the graph with instances of component implementations, the components themselves need to declare which scope they intend to represent. For example, it wouldn’t make any sense to have a @Singleton binding and a @RequestScoped binding in the same component because those scopes have different lifecycles and thus must live in components with different lifecycles. To declare that a component is associated with a given scope, simply apply the scope annotation to the component interface.

上面的这句话细究有点费解,通过下面的例子,可以知道其大略意思是:Dagger 2将有范围限制的实例和component的实例联系到了一起,component需要知道这个范围。举例来说,将一个将一个@Singleton绑定(单例对象)和一个@RequestScoped绑定放到一个component中是没有任何意义的,因为他们拥有不同的生命周期,因此必须放到拥有不同生命周期的component中。声明一个component和一个指定的范围相关,只需要将范围注解添加到接口上。

@Component(modules = DripCoffeeModule.class)
@Singleton
interface CoffeeShop {
  CoffeeMaker maker();
}

Components may have multiple scope annotations applied. This declares that they are all aliases to the same scope, and so that component may include scoped bindings with any of the scopes it declares.

Components可以有多个范围注解。这说明他们有相同的范围,因此component可以使用他们中任何一个包含这个范围。

Reusable scope

Sometimes you want to limit the number of times an @Inject-constructed class is instantiated or a @Provides method is called, but you don’t need to guarantee that the exact same instance is used during the lifetime of any particular component or subcomponent. This can be useful in environments such as Android, where allocations can be expensive.

这句话的意思是,当你可以限制一个类被创建的次数或者一个@Provides方法被调用的次数,同时你不需要保证在生命周期中同一时间使用的是某个特定的component或者其子类的同一个对象。在Android中,创建一个很耗费资源的对象时,这种使用方式很有帮助,

For these bindings, you can apply @Reusable scope. @Reusable-scoped bindings, unlike other scopes, are not associated with any single component; instead, each component that actually uses the binding will cache the returned or instantiated object.

通过使用@Reusable注解你可以使用这种绑定。@Reusable绑定和其他绑定不一样,和@Singleton绑定没有联系。取而代之的是,每个使用@Reusable 的component将会缓存返回的或者实例化的对象。

That means that if you install a module with a @Reusable binding in a component, but only a subcomponent actually uses the binding, then only that subcomponent will cache the binding’s object. If two subcomponents that do not share an ancestor each use the binding, each of them will cache its own object. If a component’s ancestor has already cached the object, the subcomponent will reuse it.

这意味着,如果你加载了一个@Reusable注解的component,但是只有他的子类使用了这个bbinding,那么只有子类会缓存这个对象。如果2个不共享一个祖先的子类,每个都单独使用了这种binding,那么他们各自缓存各自的对象。如果一个component的祖先已经缓存了这个对象,那么这个子component将会直接复用这个对象。

There is no guarantee that the component will call the binding only once, so applying @Reusable to bindings that return mutable objects, or objects where it’s important to refer to the same instance, is dangerous. It’s safe to use @Reusable for immutable objects that you would leave unscoped if you didn’t care how many times they were allocated.

这句话大概意思是说:component有可能多次调用binding,所以在返回值可变对象或者返回值要求严格是同一个对象的bindings上使用@Reusable是很危险的事。在无范围的要求的不可变对象上使用@Reusable是很安全的,如果你不在乎创建多少次的话。

@Reusable // It doesn't matter how many scoopers we use, but don't waste them.
class CoffeeScooper {
  @Inject CoffeeScooper() {}
}

@Module
class CashRegisterModule {
  @Provides
  @Reusable // DON'T DO THIS! You do care which register you put your cash in.
            // Use a specific scope instead.
  static CashRegister badIdeaCashRegister() {
    return new CashRegister();
  }
}

@Reusable // DON'T DO THIS! You really do want a new filter each time, so this
          // should be unscoped.
class CoffeeFilter {
  @Inject CoffeeFilter() {}
}
Lazy injections

Sometimes you need an object to be instantiated lazily. For any binding T, you can create a Lazy which defers instantiation until the first call to Lazy’s get() method. If T is a singleton, then Lazy will be the same instance for all injections within the ObjectGraph. Otherwise, each injection site will get its own Lazy instance. Regardless, subsequent calls to any given instance of Lazy will return the same underlying instance of T.

有时候你可能需要一个对象在使用到的时候再初始化。通过使用Lazy 可以确保在第一次调用Lazy’的get()方法才初始化。如果T是一个单例,Lazy对于整个图来说将是同一个实例。否则每次调用将会得到不同的Lazy实例。不受后续调用的影响,Lazy底层返回的始终是同一个T。

class GrindingCoffeeMaker {
  @Inject Lazy<Grinder> lazyGrinder;

  public void brew() {
    while (needsGrinding()) {
      // Grinder created once on first call to .get() and cached.
      lazyGrinder.get().grind();
    }
  }
}
Provider injections

Sometimes you need multiple instances to be returned instead of just injecting a single value. While you have several options (Factories, Builders, etc.), one option is to inject a Provider instead of just T. A Provider invokes the binding logic for T each time .get() is called. If that binding logic is an @Inject constructor, a new instance will be created, but a @Provides method has no such guarantee.

有时候你需要返回多个实例,而不仅仅是同一个对象。那么你有几种选择(工厂,构建者等等),还有一个选择是使用Provider代替T。每次调用.get()时Provider都会执行一遍binging逻辑。如果那个binging逻辑是一个@Inject主节点额构造方法,一个新的对象将被创建。但是@Provides则不提供这种保证。

class BigCoffeeMaker {
  @Inject Provider<Filter> filterProvider;

  public void brew(int numberOfPots) {
  ...
    for (int p = 0; p < numberOfPots; p++) {
      maker.addFilter(filterProvider.get()); //new filter every time.
      maker.addCoffee(...);
      maker.percolate();
      ...
    }
  }
}

Qualifiers

Sometimes the type alone is insufficient to identify a dependency. For example, a sophisticated coffee maker app may want separate heaters for the water and the hot plate.

有时一个类型不足以区别一个依赖。例如一个复杂的咖啡应用,也许需要将加热器区分为 水加热器 和 热板加热器。

You can create your own qualifier annotations, or just use @Named. Apply qualifiers by annotating the field or parameter of interest. The type and qualifier annotation will both be used to identify the dependency.

你可以创建自己的限定词注解,也可以直接使用@Named注解。通过在感兴趣的field或参数上使用注解添加限定词。类型和限定词注解将会一起被用于区分依赖。

class ExpensiveCoffeeMaker {
  @Inject @Named("water") Heater waterHeater;
  @Inject @Named("hot plate") Heater hotPlateHeater;
  ...
}

Supply qualified values by annotating the corresponding @Provides method.在合适的@Provides 注解的方法上使用限定词

@Provides @Named("hot plate") static Heater provideHotPlateHeater() {
  return new ElectricHeater(70);
}

@Provides @Named("water") static Heater provideWaterHeater() {
  return new ElectricHeater(93);
}

下面的用法没用过,看起来也不是很明白,暂且把官网的粘过来吧,有兴趣的小伙伴可以研究下

Optional bindings

If you want a binding to work even if some dependency is not bound in the component, you can add a @BindsOptionalOf method to a module:

@BindsOptionalOf abstract CoffeeCozy optionalCozy();

That means that @Inject constructors and members and @Provides methods can depend on an Optional object. If there is a binding for CoffeeCozy in the component, the Optional will be present; if there is no binding for CoffeeCozy, the Optional will be absent.

Specifically, you can inject any of the following:

Optional (unless there is a @Nullable binding for CoffeeCozy; see below)
Optional<Provider>
Optional<Lazy>
Optional<Provider<Lazy>>
(You could also inject a Provider or Lazy or Provider of Lazy of any of those, but that isn’t very useful.)

If there is a binding for CoffeeCozy, and that binding is @Nullable, then it is a compile-time error to inject Optional, because Optional cannot contain null. You can always inject the other forms, because Provider and Lazy can always return null from their get() methods.

An optional binding that is absent in one component can be present in a subcomponent if the subcomponent includes a binding for the underlying type.

You can use either Guava’s Optional or Java 8’s Optional.

下面的感觉用的概率也不是很大,知道可以接受args中的参数就可以了,等用到再研究吧。

Binding Instances

Often you have data available at the time you’re building the component. For example, suppose you have an application that uses command-line args; you might want to bind those args in your component.

Perhaps your app takes a single argument representing the user’s name that you’d like to inject as @UserName String. You can add a method annotated @BindsInstance to the component builder to allow that instance to be injected in the component.

@Component(modules = AppModule.class)
interface AppComponent {
  App app();

  @Component.Builder
  interface Builder {
    @BindsInstance Builder userName(@UserName String userName);
    AppComponent build();
  }
}

Your app then might look like

public static void main(String[] args) {
  if (args.length > 1) { exit(1); }
  App app = DaggerAppComponent
      .builder()
      .userName(args[0])
      .build()
      .app();
  app.run();
}

In the above example, injecting @UserName String in the component will use the instance provided to the Builder when calling this method. Before building the component, all @BindsInstance methods must be called, passing a non-null value (with the exception of @Nullable bindings below).

If the parameter to a @BindsInstance method is marked @Nullable, then the binding will be considered “nullable” in the same way as a @Provides method is nullable: injection sites must also mark it @Nullable, and null is an acceptable value for the binding. Moreover, users of the Builder may omit calling the method, and the component will treat the instance as null.

@BindsInstance methods should be preferred to writing a @Module with constructor arguments and immediately providing those values.

Compile-time Validation

The Dagger annotation processor is strict and will cause a compiler error if any bindings are invalid or incomplete. For example, this module is installed in a component, which is missing a binding for Executor:

这句话是说,Dagger的注解进程很严格,如果任何bindings是无效的或者没有完成,都会抛出一个编译错误。下面是一个缺少Executor的构建的错误。

@Module
class DripCoffeeModule {
  @Provides static Heater provideHeater(Executor executor) {
    return new CpuHeater(executor);
  }
}

When compiling it, javac rejects the missing binding:

[ERROR] COMPILATION ERROR :
[ERROR] error: java.util.concurrent.Executor cannot be provided without an @Provides-annotated method.

Fix the problem by adding an @Provides-annotated method for Executor to any of the modules in the component. While @Inject, @Module and @Provides annotations are validated individually, all validation of the relationship between bindings happens at the @Component level. Dagger 1 relied strictly on @Module-level validation (which may or may not have reflected runtime behavior), but Dagger 2 elides such validation (and the accompanying configuration parameters on @Module) in favor of full graph validation.

通过添加一个@Provides注解的方法,返回Executor可以解决上面的问题。Dagger 1是在Module层级检验有效行性的。Dagger 2缺少了Moudle层级的验证,转而改为整个图的验证。

Compile-time Code Generation

Dagger’s annotation processor may also generate source files with names like CoffeeMaker_Factory.java or CoffeeMaker_MembersInjector.java. These files are Dagger implementation details. You shouldn’t need to use them directly, though they can be handy when step-debugging through an injection. The only generated types you should refer to in your code are the ones Prefixed with Dagger for your component.

Dagger的注解进程也许同时生产名字类似CoffeeMaker_Factory.java或CoffeeMaker_MembersInjector.java。这些文件是Dagger 内部的实现细节,你不应该直接使用他们,尽管手动调试的时候这些可以很有用。你应该直接使用的唯一类的文件是以Dagger开头的文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值