Dagger使用详解

Dagger 使用

电脑与CPU

开门见山,来看一段常用业务代码。

以电脑为例。每一台电脑都需要CPU。所以在定电脑的时候,需要将CPU作为参数传入。

public class Computer {
    CPU cpu;

    public Computer(CPU cpu) {
        this.cpu = cpu;
    }

    public CPU getCpu() {
        return this.cpu;
    }
}
public class CPU {
    public CPU() {

    }

    public void run() {
        System.out.println("CPU 运行起来了");
    }
}

运行示例:

Computer car = new Computer(new CPU());
car.getCpu().run();

可以看到,我们如果要初始化Car对象,需要手动以构造的方式传入Engine对象,Car 与Engine的耦合严重。

Dagger2

其实Dagger2起源于Dagger ,是google基于 Dagger 优化之后,基于Java 注解的开源库。Dagger 2 会在编译阶段完成代码的生成。

主要用于模块间的解耦合,提高代码质量等。

为了解决这些问题,Dagger2应运而生。

其实说白了,Dagger2 是基于APT(annotation processing tool 注解处理工具)工具实现(Arouter),后续有空的话,咱们再聊聊APT。这次,咱先说说Dagger2的一些用注解。

常用注解

@Inject 注入

这个注解,顾名思义,就是注入,那就是用来注解被注入的对象。

我们可以想像成打针,就是要把液体注入到患者体内(将CPU 注入到电脑内)。

那以上面的电脑与CPU为例,则CPU是被注入的对象。则咱们得在CPU的构造函数上加上@Inject的注解。

public class CPU {
    @Inject
    public CPU() {

    }

    public void run() {
        System.out.println("CPU 运行起来了");
    }
}

@Component 组件

接下来,我们需要创建一个用@Component标注的接口ComputerComponent,这个ComputerComponent其实就是一个注入器,用于将CPU注入到电脑中。

@Component
public interface ComputerComponent {
    /**
     * 这个方法的的名字可以自己主,但是因为这个是注入器,所以比较习惯命名为inject
     *
     * @param computer 注入的对象(target)
     */
    void inject(Computer computer);
}

最后,我们的Computer类,就改造成这样。

public class Computer {
    @Inject
    CPU cpu;

    public Computer() {
        DaggerComputerComponent.builder().build().inject(this);
    }

    public CPU getCpu() {
        return this.cpu;
    }
}

编写测试代码

public class Main {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.getCpu().run();
    }
}

@Module 与 @Provides

在之前的示例中,由于CPU这个类是我们自己创建的,所以可以很简单的在其构造函数上加上@Inject注解。

那如果说,我们现在不能直接修改CPU这个类,那这个时候需要怎么办?

这个时候 ,我们就用到@Module与 @Provides注解 。

其实,Module 可以理解为一个模块中,所有依赖对像的集合。或者说,被@Module注解的类,充当着管理该Module的依赖的作用。

而Provides,从字面意思就知道,就是提供(依赖的对象的)。

首先,我们先去掉CPU构造函数的注解。(因为我们此时假设,这两个类是不能让我们直接修改的。)

public class CPU {
    public CPU() {

    }

    public void run() {
        System.out.println("CPU 运行起来了");
    }
}

接下来,新建一个ComputerModle类。

@Module
public class ComputerModule {

    @Provides
    CPU providerCPU(){
        return new CPU();
    }
}

接下来,修改之前的ComputerComponent。

@Component(modules = ComputerModule.class)
public interface ComputerComponent {
    /**
     * 这个方法的的名字可以自己主,但是因为这个是注入器,所以比较习惯命名为inject
     *
     * @param computer 注入的对象(target)
     */
    void inject(Computer computer);
}

可以看到,components 后面的参数其实是一个module集合,也就是说,我们可以通过@Component,用来管理多个modules。

之前说过,@Module是用来管理依赖的,我们假设现在有一个Keyborad类,然后Computer类需要依赖该类,则ComputerModule类可以写成这样。

@Module
public class ComputerModule {

    @Provides
    CPU providerCPU(){
        return new CPU();
    }
    @Provides
    Keyboard providerKeyBord(){
        return new Keyboard();
    }
}

@Qualifier

那么现在又有一种情况,假如我的电脑需要有一个外接键盘以前一个自带的键盘(对,我的电脑是笔记本!问就是macbook pro),此时,我们该如何解决?

这个时候,@Qualifier就可以派上用场了。

首先,我们利用该注解,去定义两个注解。

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface QualifierOne {
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface QualifierTwo {
}

接下来,我们修改一下Module。

@Module
public class ComputerModule {

    @Provides
    CPU providerCPU(){
        return new CPU();
    }
    @QualifierOne
    @Provides
    Keyboard providerKeyBordOr(){
        return new Keyboard("原生");
    }
    @QualifierTwo
    @Provides
    Keyboard providerKeyBordOut(){
        return new Keyboard("外接");
    }
}

然后就是Computer的代码了。

public class Computer {
    @Inject
    CPU cpu;

    @QualifierOne
    @Inject
    Keyboard keyBord1;
    @QualifierTwo
    @Inject
    Keyboard keyBord2;

    public Computer() {
        DaggerComputerComponent.builder().build().inject(this);
    }

    public CPU getCpu() {
        return this.cpu;
    }

    public Keyboard getKeyBord1() {
        return keyBord1;
    }

    public Keyboard getKeyBord2() {
        return keyBord2;
    }
}

接下来就是测试代码:

        Computer computer = new Computer();
        computer.getCpu().run();
        computer.getKeyBord1().install();
        computer.getKeyBord2().install();

可以看到,Qualifier的作用,其实就是,为依赖方指定生成方式。

@Scope 作用域

通过该注解, 我们可以实现局部单例。

首先,我们先通过@Scope,定义一个ComputerScope注解。

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ComputerScope {
}

之后,我们用该注解,去标记之前的依赖提供者。在该示例中,我标记的是提供CPU的方法。

@Module
public class ComputerModule {

    @ComputerScope
    @Provides
    CPU providerCPU(){
        return new CPU();
    }
    @QualifierOne
    @Provides
    Keyboard providerKeyBordOr(){
        return new Keyboard("原生");
    }
    @QualifierTwo
    @Provides
    Keyboard providerKeyBordOut(){
        return new Keyboard("外接");
    }
}

同时,我们也需要对Component进行标记。

@ComputerScope
@Component(modules = ComputerModule.class)
public interface ComputerComponent {
    /**
     * 这个方法的的名字可以自己主,但是因为这个是注入器,所以比较习惯命名为inject
     *
     * @param computer 注入的对象(target)
     */
    void inject(Computer computer);
}

最后,我们简单修改一下Computer类,让其拥有两个CPU的成员变量。

public class Computer {
    @Inject
    CPU cpu;
    @Inject
    CPU cpu2;

    @QualifierOne
    @Inject
    Keyboard keyBord1;
    @QualifierTwo
    @Inject
    Keyboard keyBord2;

    public Computer() {
        DaggerComputerComponent.builder().build().inject(this);
    }

    public CPU getCpu() {
        return this.cpu;
    }

    public Keyboard getKeyBord1() {
        return keyBord1;
    }

    public Keyboard getKeyBord2() {
        return keyBord2;
    }
}

为了便于测试,我们简单修改一下CPU。

public class CPU {
    public CPU() {
        System.out.println("初始化CPU");
    }

    public void run() {
        System.out.println("CPU 运行起来了");
    }
}

测试代码:

        Computer computer = new Computer();
        computer.getCpu().run();
        System.out.println(computer.getCpu().hashCode());
        System.out.println(computer.getCpu2().hashCode());
        System.out.println(computer.getCpu2().equals(computer.getCpu()));

输出

初始化CPU
CPU 运行起来了
1956725890
1956725890
true

可见,此时,只实例化了一个CPU对象。

@Singleton 单例

其实一开始,我以为这个可能和我们平时写的单例是一个意思,直到我点开他的源码。

/**
 * Identifies a type that the injector only instantiates once. Not inherited.
 *
 * @see javax.inject.Scope @Scope
 */
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

是不是和咱们之前写的@ComputerScope 一毛一样。emmm,所以,功能也一样了,可以用来实现局部单例。

那至于如何实现全局单例,这个可以放到后面的Dagger Android中来讨论。

@Binds

电脑的CPU,键盘都有了,现在需要为电脑提供一个屏幕了。此时,我们希望屏幕的具体类型通过参数的形式传入。

@Module
public class ComputerModule {
    
    @Provides
     IScreen providerScreen(AppleScreen screen){
        return screen;
    }
}

但如果用@Bind注解,我们可以让这个看起来更好看。

@Module
public abstract class ComputerModule {
    @Binds
    abstract IScreen bindAppScreen(AppleScreen screen);
}

需要注意的是,如果用@Bind注解,需要将该类定义为抽象类,并且被@Bind注解的方法为抽象方法。同时,参数 AppleScreen ,需要是

返回值ISreen的实现。

再然后,AppleScreen 的构造函数,需要被@Inject标记。

public class AppleScreen implements IScreen{

    @Inject
    public AppleScreen() {
    }

    @Override
    public void show() {
        System.out.println("苹果显示器");
    }
}

当然,如果说想让@Provides与 @Bind在一个@Module中共存,则需要将@Provides 标记的方法的修饰符上加上 static,否则,运行是会报错的。

@Module
public abstract class ComputerModule {

    @ComputerScope
    @Provides
    static CPU providerCPU(){
        return new CPU();
    }
    @QualifierOne
    @Provides
    static Keyboard providerKeyBordOr(){
        return new Keyboard("原生");
    }
    @QualifierTwo
    @Provides
     static Keyboard providerKeyBordOut(){
        return new Keyboard("外接");
    }
    @Binds
    abstract IScreen bindAppScreen(AppleScreen screen);

}

其它

Lazy 延迟注入

其实这个看他的类名就知道其作作是啥了。具体使用也比较简单。

public class Computer {
    @Inject
    Lazy<CPU> cpu;
    @Inject
    Lazy<CPU> cpu2;
    @QualifierOne
    @Inject
    Keyboard keyBord1;
    @QualifierTwo
    @Inject
    Keyboard keyBord2;

    @Inject
    AppleScreen appleScreen;

    public Computer() {
        DaggerComputerComponent.builder().build().inject(this);
    }

    public CPU getCpu() {
        return this.cpu.get();
    }

    public Keyboard getKeyBord1() {
        return keyBord1;
    }

    public Keyboard getKeyBord2() {
        return keyBord2;
    }

    public CPU getCpu2() {
        return cpu2.get();
    }

    public AppleScreen getAppleScreen() {
        return appleScreen;
    }
}

我们简单的把Computer修改成上面的样子。

然后在编写测试代码:

 Computer computer = new Computer();
        computer.getAppleScreen().show();
        computer.getCpu2().run();

输出:

苹果显示器
初始化CPU
CPU 运行起来了

可以看到,此时,CPU是在我们调用之后才初始化的。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值