Dagger系列(一)
Dagger 是一个对象注入框架,我们用采用编写接口框架自动生成实现类的方式来生成实现代码,我们只需要通过相关注解来注入对象:
举个例子,以下举例采用官方的ATM demo:
第一步加入依赖
implementation 'com.google.dagger:dagger:2.22.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
第二步 创建一个CommandRouterFactory 接口
package union.com.myapplication.lesson2;
import dagger.Component;
@Component
public interface CommandRouterFactory {
CommandRouter router();
}
@Component
这个注解是告诉dagger 实现一个接口或者是抽象类返回一个或多个对象,Dagger生成对象的名称
Dagger"yourType" 或者嵌入类(Dagger"youType"_“nestType”)
第三部 创建一个 CommandRouter 类
package union.com.myapplication.lesson2;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
public class CommandRouter {
@Inject
public CommandRouter() {
}
private final Map<String, Command> commands = Collections.emptyMap();
Command.Status route(String input) {
List<String> splitInput = split(input);
if (splitInput.isEmpty()) {
return invalidCommand(input);
}
String commandKey = splitInput.get(0);
Command command = commands.get(commandKey);
if (command == null) {
return invalidCommand(input);
}
Command.Status status =
command.handleInput(splitInput.subList(1, splitInput.size()));
if (status == Command.Status.INVALID) {
System.out.println(commandKey + ": invalid arguments");
}
return status;
}
private Command.Status invalidCommand(String input) {
System.out.println(
String.format("couldn't understand \"%s\". please try again.", input));
return Command.Status.INVALID;
}
// Split on whitespace
private static List<String> split(String string) {
return new ArrayList<>();
}
}
@Inject
该注解必须添加 用户告诉dagger 如何实例化一个对象
添加完成之后我们就可以rebuild 项目会自动生成实现类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PGeP3Bvo-1572332858446)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1572321927116.png)]
DaggerCommandRouterFactory 为自动生成的类:
// Generated by Dagger (https://google.github.io/dagger).
package union.com.myapplication.lesson2;
public final class DaggerCommandRouterFactory implements CommandRouterFactory {
private DaggerCommandRouterFactory() {}
public static Builder builder() {
return new Builder();
}
public static CommandRouterFactory create() {
return new Builder().build();
}
@Override
public CommandRouter router() {
return new CommandRouter();
}
public static final class Builder {
private Builder() {}
public CommandRouterFactory build() {
return new DaggerCommandRouterFactory();
}
}
}
第四步 带参数的注入
我们编写一个HelloWorldCommand 实现Command 接口
package union.com.myapplication.lesson2;
import java.util.List;
import javax.inject.Inject;
public class HelloWorldCommand implements Command {
@Inject
public HelloWorldCommand() {
}
@Override
public String key() {
return "Hello";
}
@Override
public Status handleInput(List<String> input) {
if (!input.isEmpty()) {
return Status.INVALID;
}
System.out.println("world!");
return Status.HANDLED;
}
}
修改CommandRouter 类:
package union.com.myapplication.lesson2;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
public class CommandRouter {
private final Map<String, Command> commands = Collections.emptyMap();
@Inject
public CommandRouter(HelloWorldCommand command){
commands.put(command.key(),command);
}
Command.Status route(String input) {
List<String> splitInput = split(input);
if (splitInput.isEmpty()) {
return invalidCommand(input);
}
String commandKey = splitInput.get(0);
Command command = commands.get(commandKey);
if (command == null) {
return invalidCommand(input);
}
Command.Status status =
command.handleInput(splitInput.subList(1, splitInput.size()));
if (status == Command.Status.INVALID) {
System.out.println(commandKey + ": invalid arguments");
}
return status;
}
private Command.Status invalidCommand(String input) {
System.out.println(
String.format("couldn't understand \"%s\". please try again.", input));
return Command.Status.INVALID;
}
// Split on whitespace
private static List<String> split(String string) {
return new ArrayList<>();
}
}
看到没有在构造方法中添加了一个HelloWorldCommand的构造方法,同时要告诉框架怎么去创建HelloWorldCommand ,因此 HelloWorldCommand 中的构造方法中的Inject 是不能少的
看下这次dagger 框架生成的和上次生成的实现类有什么不一样的:
// Generated by Dagger (https://google.github.io/dagger).
package union.com.myapplication.lesson2;
public final class DaggerCommandRouterFactory implements CommandRouterFactory {
private DaggerCommandRouterFactory() {}
public static Builder builder() {
return new Builder();
}
public static CommandRouterFactory create() {
return new Builder().build();
}
@Override
public CommandRouter router() {
return new CommandRouter(new HelloWorldCommand());
}
public static final class Builder {
private Builder() {}
public CommandRouterFactory build() {
return new DaggerCommandRouterFactory();
}
}
}
注意:CommandRouter 类中的构造方法需要穿具体的实现对象 那么如果要传入一个接口怎么办呢
第五步参数传递对象的接口
如果传递参数为对象的接口那么 dagger框架是无法知晓具体要实例化的对象
我们可以这样做
创建一个HelloWorldModule:
package union.com.myapplication.lesson2.module;
import dagger.Binds;
import dagger.Module;
import union.com.myapplication.lesson2.Command;
import union.com.myapplication.lesson2.HelloWorldCommand;
@Module
public abstract class HelloWorldModule {
@Binds
abstract Command helloWorldCommand(HelloWorldCommand command);
}
多了两个注释 @Module @Binds
这个@ Binds 方法告诉Dagger,当某些事情依赖于某个命令时,Dagger应该提供一个HelloWorldCommand对象来代替它。注意,方法的返回类型Command是Dagger现在知道如何提供的类型,而参数类型是Dagger知道在依赖于Command时使用的类型。
上面的方法之所以是抽象方法是告诉dagger 做什么,dagger并不提供该方法的实现和调用
那么 @Module 是什么东西呢
@Module 是@Binds 绑定方法的集合 必须放在 @Module 中
module 创建好了,下一步使用它
package union.com.myapplication.lesson2;
import dagger.Component;
@Component(modules = HelloWorldCommand.class)
public interface CommandRouterFactory {
CommandRouter router();
}
我们再看看dagger 为我们生成的实现类
// Generated by Dagger (https://google.github.io/dagger).
package union.com.myapplication.lesson2;
public final class DaggerCommandRouterFactory implements CommandRouterFactory {
private DaggerCommandRouterFactory() {}
public static Builder builder() {
return new Builder();
}
public static CommandRouterFactory create() {
return new Builder().build();
}
@Override
public CommandRouter router() {
return new CommandRouter(new HelloWorldCommand());
}
public static final class Builder {
private Builder() {}
public CommandRouterFactory build() {
return new DaggerCommandRouterFactory();
}
}
}
第六步 扩展HelloWorldCommand
中的打印方法:
定义一个 Outputter 接口
package union.com.myapplication.lesson2;
public interface Outputter {
void output(String output);
}
修改 HelloWorldCommand:
package union.com.myapplication.lesson2;
import java.util.List;
import javax.inject.Inject;
import dagger.Module;
public class HelloWorldCommand implements Command {
private final Outputter outputter;
@Inject
HelloWorldCommand(Outputter outputter) {
this.outputter = outputter;
}
@Override
public String key() {
return "Hello";
}
@Override
public Status handleInput(List<String> input) {
if (!input.isEmpty()) {
return Status.INVALID;
}
outputter.output("world!");
return Status.HANDLED;
}
}
OutPutter 是一个接口,我们需要提供一个实现类 然后使用Inject 来告诉dagger框怎么生成实现具体对象 然后使用 @Binds 方法绑定实现。Ok dagger 框架给我们提供了另外一种方法
@Module
abstract class SystemOutModule {
@Provides
static Outputter textOutputter() {
return System.out::println;
}
}
是不是用 Privides 注解代替了@binds 注解直接实现了方法呢
我们看下生成类:
// Generated by Dagger (https://google.github.io/dagger).
package union.com.myapplication.lesson2;
import union.com.myapplication.lesson2.module.SystemOutModule_TextOutputterFactory;
public final class DaggerCommandRouterFactory implements CommandRouterFactory {
private DaggerCommandRouterFactory() {}
public static Builder builder() {
return new Builder();
}
public static CommandRouterFactory create() {
return new Builder().build();
}
private HelloWorldCommand getHelloWorldCommand() {
return new HelloWorldCommand(SystemOutModule_TextOutputterFactory.textOutputter());
}
@Override
public CommandRouter router() {
return new CommandRouter(getHelloWorldCommand());
}
public static final class Builder {
private Builder() {}
public CommandRouterFactory build() {
return new DaggerCommandRouterFactory();
}
}
}
SystemOutModule_TextOutputterFactory 这类是不是被注入进去了?
对了忘记说了一个点:
CommandRouterFactory 不要忘记了依赖
@Component(modules = {HelloWorldModule.class, SystemOutModule.class})
public interface CommandRouterFactory {
CommandRouter router();
}
好了这节就先到这,下次继续要不这篇内容有点多