发布于:2021-01-11 11:31:11
0
89
0
Dagger爱好者Sven Ruppert在一个简单的示例中演示了依赖注入,该示例旨在声明依赖,指定如何满足依赖以及让您专注于有趣的类。
Dagger是基于Guice的开源依赖项注入(DI)框架。但是,Dagger的开发人员对Guice的基本原理并不满意:他们不得不一次又一次地在较大的项目中编写代码,其中涉及大量的绑定代码。由于这是静态语义的一部分,因此他们的策略是提取此绑定并将其以源代码形式存储。
Dagger开发人员还需要易于阅读且支持调试过程的内容。下面我们更仔细地研究Dagger并开始探索该框架。我们的示例基于 Dagger 1.2版。
用Dagger进行依赖注入
Dagger的怪癖之一是其在Java和Android系统中的可用性。让我们看一个简单的示例,并从Main类中的简单@Inject实例(Main Service)开始:
public class Main { @Inject MainService mainService; /.. } public interface MainService { public String execute(String txt); } public class MainServiceImpl implements MainService { public String execute(String txt){ return "MainServiceImpl _ " + txt; } }
为了获得具有注入属性的类的实例,需要使用ObjectGraph。ObjectGraph是实例化所必需的所有依赖项的表示。在下面的示例中,我们可以在Main类中看到Dagger的初始化和使用:
public class Main { @Inject MainService mainService; public static void main(String[] args) { //bootstrapping ObjectGraph objectGraph = ObjectGraph.create(new BusinessModule()); Main main = objectGraph.get(Main.class); // Main main = new Main(); // objectGraph.inject(main); System.out.println("execute = " + main.mainService.execute("Go")); System.out.println("execute = " + main.mainService.execute("Go")); System.out.println("execute = " + main.mainService.execute("Go")); } }
Dagger通过两种方式访问包含注入的属性的类的实例。一种方法是通过ObjectGraph类的get(..)方法直接提供一个就绪实例。另一种方法是通过显式调用方法inject(..) ,该方法可以应用于现有实例。这使您可以控制在生成保持类实例与成员本身之间的时间。
注意事项:为了生成ObjectGraph类的实例,需要create(..)方法的参数 或模块。在我们的例子中,我们创建了 BusinessModule()。在此模块中,定义了它们之间的依赖关系,还使用了生产者。
@Module(library = false, injects = {Main.class}, complete = true) public class BusinessModule { @Provides MainService provideMainService() { return new MainServiceImpl(); } }
生产者既可以在实例本身中生成,也可以在方法参数上使用注入(如下)。请注意,Dagger必须使用新的构造函数-如果要创建的实例本身没有注入的属性-或已通过带有注入属性的方法参数传递了实例。
@Provides MainService provideMainService(MainServiceImpl mainService) { return mainService; }
内部功能
开发人员很快就会提出的基本问题涉及到依赖项的定义。Java中有几种定义依赖关系的方法–如果依赖关系很明确,则通常很容易。当涉及区分或多重性时,它将变得更加复杂。
以类似SubService的接口为例 。对于此接口,有两种实现:SubServiceA和SubServiceB。如果我们将 @Inject SubService 放在源代码中,则实现不清楚。在下文中,我们假定它将始终生成一个实例,该实例已使用注释@Produces管理了相应的方法ProvideXXX(在我们的示例中为offerMainService())。这是工厂方法。
public class MainServiceImpl implements MainService { @Inject SubService subService; public String execute(String txt){ return subService.work(txt); } }
还需要一种用于创建SubService实例的工厂方法。在这里,您可以选择应使用的实现(SubServiceA或 SubServiceB)。当然,这是一个静态的决定。在稍后的阶段,我们将使其动态化。
@Provides SubService provideSubService() { return new SubServiceA(); }
反射和配置
但是Dagger可以识别在哪里使用哪个实例吗?反射或配置是这里的答案。在某些框架中,使用配置方法。然后读取此配置,并将其用作实例化的基础。不幸的是,由于这些方法麻烦且容易出错,因此可能会出现问题。
另一种选择是反射。在运行时,我们可以确定哪个类具有依赖关系,但是,在这种情况下,我们尚未解决多重性问题。 为此,JSR-330具有限定符@Named(..)。这将允许您输入相应的实现名称,然后在解析中使用它们。 SubServiceA 将被实现为 @Named(“A”)和SubServiceB与@Named(“B”) 。一旦指定了限定词,我们就定义了 @Inject:
@Inject @Named("A") SubService subservice;
@Named(“ A”)也提供了随附的工厂方法。这是关于将类/接口和注释相结合,然后进行匹配的所有内容:
@Inject SubService subservice
您不要忘记,仍然需要编写没有限定符的工厂方法。
@Module(library = false, injects = {Main.class},complete = true) public class BusinessModule { @Provides MainService provideMainService(MainServiceImpl mainService) { return mainService; } @Provides @Named("A") SubService provideSubServiceA() { return new SubServiceA(); } @Provides @Named("B") SubService provideSubServiceB() { return new SubServiceB(); } @Provides SubService provideSubService(@Named("A") SubService subServiceA) { return subServiceA; } }
现在如何以及何时检测到依赖关系?这可以使用反射在运行时完成。Dagger是一个略有不同的框架:使用了反射,但它不是运行时,而是参与了编译过程。使用反射解决依赖关系并进行检查,此过程会产生不同的结果。首先,具有扩展名的文件点应用,在那里你可以阅读有关发现的依赖关系。请参阅以下内容:
digraph G1 { concentrate = true; Main -> MainService; MainService -> MainServiceImpl; MainServiceImpl -> SubService; n2 [label="@javax.inject.Named(value=A) /org.rapidpm.demo.dagger.business.subservice.SubService"]; SubService -> n2; }
这些化合物很容易识别。但是生成了哪些类?这是指:Main $$ InjectAdapter,MainServiceImpl $$ InjectAdapter和BusinessModule $$ ModuleAdapter。
让我们从Main $$ InjectAdapter类开始。适配器提供了一个get()方法,该方法返回一个已实例化的引用。在这里,您可以看到确切的生命周期-而且很容易管理。它包括两个步骤:首先,创建实例,其次,合并injectMembers(..)方法。
此外,该化合物还适用于其他类别。为了确保所有内容都保持静态语义,并在运行时调用相应的Getter。与Reflection相比,此方法在运行时效率更高。因此,此适配器类形成了完整的绑定,其中包括应用程序的所有组合和变体。
作者介绍