dagger 生成_深入研究Dagger生成的代码第1部分

dagger 生成

Dagger is one of the most complex DI frameworks for Java and Android out there. It comes with compile-time safety and fast runtime but has a steep learning curve and requires a lot of boilerplate code to setup. When using Dagger, our goal is to set it up to generate the code that would solve our DI needs. By understanding the code Dagger generates we can better understand how to configure it properly.

Dagger是目前最复杂的Java和Android DI框架之一。 它具有编译时安全性和快速运行时间,但学习曲线陡峭,并且需要大量样板代码进行设置。 使用Dagger时,我们的目标是对其进行设置以生成可满足DI需求的代码。 通过了解Dagger生成的代码,我们可以更好地了解如何正确配置它。

One of the problems is that the generated Dagger code can be quite verbose. Null-checks, explanatory messages and a lot of generated classes are essential for debugging and making it work in different scenarios but complicate the understanding when you read or debug it. Also, Dagger generates code in Java, which is by itself quite verbose.

问题之一是生成的Dagger代码可能非常冗长。 空检查,说明性消息和许多生成的类对于调试并使其在不同的场景中起作用至关重要,但使您在阅读或调试它时的理解变得复杂。 而且,Dagger会用Java生成代码,其本身非常冗长。

In this series, we will convert the Dagger generated code from Java to Kotlin utilizing Kotlin language features to refactor and greatly simplify it for a better understanding of how it works.

在本系列中,我们将利用Kotlin语言功能将Dagger生成的代码从Java转换为Kotlin,以对其进行重构和大大简化,以更好地了解其工作原理。

(Example)

To get familiar with the process, let’s consider the following simple setup.

为了熟悉该过程,让我们考虑以下简单设置。

When we compile this, Dagger will generate a few classes, one of which is the implementation of the component — DaggerInteractorComponent. If we convert these generated classes to Kotlin, we could get something like this.

编译此代码时,Dagger将生成一些类,其中一个是组件DaggerInteractorComponent 。 如果将这些生成的类转换为Kotlin,我们将得到类似的结果。

Dagger generates code in Java. This Kotlin snippet was obtained just by running Java to Kotlin converter
Dagger用Java生成代码。 这个Kotlin片段是通过运行Java到Kotlin转换器获得的

To simply create instances of an Interactor on-demand, Dagger generated 56 lines of code! Let’s apply some transformations to it to reduce verbosity:

为了简单地按需创建Interactor实例,Dagger生成了56行代码! 让我们对其进行一些转换以减少冗长:

  1. Remove all the null-checks and other assertions

    删除所有空检查和其他断言
  2. Replace component Builder /Factory with a primary constructor with default parameters.

    用具有默认参数的主构造函数替换组件Builder / Factory

  3. For every generated factory (such as InteractorModule_InteractorFactory), inline static methods into call sites.

    对于每个生成的工厂(例如InteractorModule_InteractorFactory ),将静态方法内联到调用站点中。

After applying these transformations, the result becomes quite concise and easy-to-understand.

应用这些转换后,结果将变得十分简洁和易于理解。

This is what the rest of the series will be about. First, we will start with some Dagger setup. Second, we’ll convert the generated code from Java to Kotlin using the rules defined above. And finally, when new code constructs are met, we will define new rules of conversion to produce even more concise code.

这就是本系列其余部分的内容。 首先,我们将从Dagger设置开始。 其次,我们将使用上面定义的规则将生成的代码从Java转换为Kotlin。 最后,当遇到新的代码构造时,我们将定义新的转换规则以生成更简洁的代码。

匕首的主要抽象 (Dagger’s main abstraction)

Object graph, generated by Dagger, is quite a complex thing. Bindings, defined in the component, can come from modules, from component’s dependencies and bound instances, from @Inject constructors, they can be scoped and unscoped. Such complexity should be simplified by using some sort of abstraction. The one Dagger heavily uses is the Provider interface, defined in JSR-330.

Dagger生成的对象图是一件相当复杂的事情。 组件中定义的绑定可以来自模块,也可以来自组件的依赖关系和绑定实例,也可以来自@Inject构造函数,它们可以是作用域和不受作用域的。 应该通过使用某种抽象来简化这种复杂性。 Dagger大量使用的一个是Provider接口,该接口在JSR-330中定义。

public interface Provider<T> {T get();
}

Provider is a simple functional interface (aka lambda), that just returns some value. It can create a new instance for every invocation of get(), or it can return the same value over and over again, or it can do both — Provider just guarantees to return some value. Dagger often uses Providers in the presence of scopes (we will take a look at them later in the series), but in their absence Dagger optimizes the generated code and avoids creating Providers if possible.

Provider是一个简单的功能接口(又名lambda),仅返回一些值。 它可以为每次get()调用创建一个新实例,或者可以一遍又一遍地返回相同的值,或者可以同时执行两种操作— Provider只是保证返回一些值。 Dagger通常在存在作用域的情况下使用Provider (我们将在本系列的后面部分对其进行介绍),但是在没有它们的情况下,Dagger会优化生成的代码,并尽可能避免创建Provider

There are many ways to add bindings to a component’s graph, to name a few:

有很多方法可以将绑定添加到组件图上,仅举几例:

  • @Inject constructors

    @Inject构造函数

  • @Provides methods in modules

    @Provides提供模块中的方法

  • component dependencies

    组件依赖性

For every one of the enumerated above ways Dagger will generate special Factory or Provider class. So let’s examine each one of them more closely.

对于以上列举的每种方式,Dagger都会生成特殊的FactoryProvider类。 因此,让我们更仔细地检查它们中的每一个。

@Inject构造函数 (@Inject constructor)

Annotating a class constructor with @Inject allows Dagger to create instances of this class without additional configuration.

使用@Inject注释类构造函数可以使Dagger创建此类的实例而无需其他配置。

class Repository @Inject constructor()

For every class with the @Inject constructor, Dagger will generate a Factory (Factory interface extends Provider defined above). Let’s take a look at the generated factory for the Repository class.

对于使用@Inject构造函数的每个类,Dagger将生成一个Factory ( Factory接口扩展了上面定义的Provider )。 让我们看看为Repository类生成的工厂。

For every class with the @Inject constructor, Dagger will generate a factory. Factory is the interface that extends Provider (javadoc). Let’s take a look at the generated factory for the Repository class.

对于使用@Inject构造函数的每个类,Dagger将生成一个工厂。 Factory是扩展Provider ( javadoc )的接口。 让我们看看为Repository类生成的工厂。

Every factory generated for the @Inject constructor has three methods. get() is a method, defined in the Provider interface, and it returns a new instance for every invocation. For that, it will delegate to the generated static method newInstance(), which creates that instance. Finally, the static create() method returns an instance of a factory.

@Inject构造函数生成的每个工厂都有三种方法。 get()是在Provider接口中定义的方法,并且每次调用都会返回一个新实例。 为此,它将委派给生成的静态方法newInstance() ,该方法创建该实例。 最后,静态create()方法返回一个工厂实例。

Static methods are created mostly for simplifying the generated code and making it compile. For example, if a class has a package-private constructor, newInstance() makes it effectively public, and thus available for all the other generated code. create() simplifies obtaining an instance of the factory. In most cases, it just calls a constructor, but when a class has no dependencies, Dagger generates a singleton factory, like the Repository_Factory, and create() method just returns the object instance.

创建静态方法主要是为了简化生成的代码并使之编译。 例如,如果一个类具有包专用的构造函数,则newInstance()使其有效地公开,从而可用于所有其他生成的代码。 create()简化了获取工厂实例的过程。 在大多数情况下,它只是调用构造函数,但是当类没有依赖关系时,Dagger会生成一个单例工厂,例如Repository_Factory ,并且create()方法仅返回对象实例。

But it is a rare situation when @Inject is applied to the constructor without parameters. So let’s add a parameter to the constructor.

但是,将@Inject应用于不带参数的构造函数的情况很少见。 因此,我们向构造函数添加一个参数。

In that case, the generated factory will look a bit different.

在这种情况下,生成的工厂看起来会有些不同。

Now generated factory accepts one Provider instance for every parameter of the injected constructor (in the case above it was only one parameter). In the overridden get() method, the factory simply creates a new instance and uses given providers to resolve dependencies of that instance.

现在生成的工厂为注入的构造函数的每个参数接受一个Provider实例(在上述情况下,它只是一个参数)。 在重写的get()方法中,工厂仅创建一个新实例并使用给定的提供程序来解析该实例的依赖关系。

Returning to the component, if we inline that factory method call into it, we get this straightforward code.

回到组件,如果我们内联该工厂方法调用,我们将获得此简单代码。

模组 (Modules)

Modules are another way to define bindings. Every method annotated with @Provides annotation will add a new binding to the component. These methods can call other methods of a module and access its properties. Let’s take a look at the simple example of a module with one @Provides method.

模块是定义绑定的另一种方法。 每个带有@Provides注释的方法都将向该组件添加新的绑定。 这些方法可以调用模块的其他方法并访问其属性。 让我们来看一个带有一个@Provides方法的模块的简单示例。

create() method implementation is out of the scope of this blog post
create()方法的实现超出了本博客文章的范围

Dagger generates one factory for every @Provides method of a module. These factories are a bit different from @Inject constructor factories. First, newInstance() methods are renamed to match the @Provides method name, like createApiService(). Second, an additional parameter of the module instance is added to the constructor of the factory. And similar to @Inject constructor factories, they accept one Provider instance for every parameter of the @Provides method.

Dagger为模块的每个@Provides方法生成一个工厂。 这些工厂与@Inject构造函数工厂有些不同。 首先,将newInstance()方法重命名以匹配@Provides方法名称,例如createApiService() 。 其次,将模块实例的附加参数添加到工厂的构造函数中。 与@Inject构造函数工厂类似,它们为@Provides方法的每个参数接受一个Provider实例。

In the snippet above, create() method was omitted because it often just calls the constructor. So let’s add a new rule of conversion:

在上面的代码段中,省略了create()方法,因为它通常只是调用构造函数。 因此,我们添加一个新的转化规则:

4. Static create() methods are inlined and replaced with a direct constructor call.

4.内联静态create()方法,并用直接构造函数调用替换。

模块中的静态方法 (Static methods in modules)

Class modules are fine when you need to convert external dependencies or untangle some complex relationships between objects. But in most cases, static methods in modules are more preferable, because they do not depend on the state of a module instance, and therefore the instance of the module is not needed to instantiate the object.

当您需要转换外部依赖关系或解开对象之间的某些复杂关系时,使用类模块很好。 但是在大多数情况下,模块中的静态方法是更可取的,因为它们不依赖于模块实例的状态,因此不需要模块的实例来实例化对象。

Note that no instance of the module passed to the constructor of the generated factory. Effectively, static @Provides methods in object modules are kinda like @Inject constructors defined externally (but they do not support generics yet).

请注意,没有模块实例传递给生成的工厂的构造函数。 实际上,对象模块中的静态@Provides方法有点像在外部定义的@Inject构造函数(但它们尚不支持泛型)。

依存关系 (Dependencies)

Component dependency is a feature that allows adding external bindings from the dependency interface. When a type is used as a component dependency, each provision method on the dependency is bound as a provider. It is used mostly to get bindings from another component, but can also be used with the arbitrary interface.

组件依赖项是一项允许从依赖项接口添加外部绑定的功能。 将类型用作组件依赖项时,依赖项上的每个提供方法都将绑定为提供程序。 它主要用于从另一个组件获取绑定,但也可以与任意接口一起使用。

If Dagger needs to create a provider for dependency, it will generate an inner Provider class for that dependency inside the component, otherwise, it will call the dependencies’ provision method directly.

如果Dagger需要为依赖关系创建提供程序,则它将在组件内部为该依赖关系生成内部Provider类,否则,它将直接调用依赖关系的提供方法。

Dagger really generates an inner class with such a long name
匕首确实会生成一个具有如此长名称的内部类

After simplifying, we get the following code.

简化后,我们得到以下代码。

第一部分结束 (End of part 1)

In this blog post, we looked at the code Dagger generates in the presence of @Inject constructors and @Provides methods and learned about Provider and Factory interfaces. You can find all of the examples in the GitHub repository. The next blog post will uncover even more features of Dagger: members-injection, module binds, qualifiers, and multibindings. In the meantime, you can check out an awesome blog post “Dagger code generation cheat sheets” from Manuel Vivo.

在此博客文章中,我们研究了Dagger在@Inject构造函数和@Provides方法存在下生成的代码,并了解了ProviderFactory接口。 您可以在GitHub存储库中找到所有示例。 下一篇博客文章将揭示Dagger的更多功能:成员注入,模块绑定,限定符和多重绑定。 同时,您可以查看Manuel Vivo的精彩博客文章“ Dagger代码生成备忘单 ”。

翻译自: https://proandroiddev.com/deep-dive-into-dagger-generated-code-part-1-58f3cb9563de

dagger 生成

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值