美文网首页
Dagger2 | 三、进阶 - @Provides

Dagger2 | 三、进阶 - @Provides

作者: mrzhqiang | 来源:发表于2020-06-30 01:16 被阅读0次

    在上一章,@Component 将项目中的类实例直接 new 出来,注入给依赖的地方。
    对于第三方库,没法用 @Inject 标记,但 Dagger2 告诉我们可以 提供依赖

    3.1 提供依赖

    根据 @ProvidesAPI 描述,我们需要用 @Module 标记一个类,然后使用 @Provides 标记返回类型。

    3.1.1 结构重构

    在尝试之前,我们需要做一点小小的结构重构:

    -java
        -com.domain.project
            -account
                *Account
            -di
                *ActivityComponent
            *MainAcitvity
    

    现在,我们专注于 com.domain.project.di 包,开始我们全新的 dependencies inject 之旅。

    3.1.2 依赖模块

    首先在 com.domian.project.di 包下,创建 AccoutModule.java 类:

    import dagger.Module;
    
    @Module final class AccountModule {
    }
    

    OOP 角度来设计,我们不准备对外暴露这个模块,并且不允许它被扩展。

    然后添加提供者方法:

    @Module final class AccountModule {
      @Provides Account provideAccount() {
        return new Account();
      }
    }
    

    通常我们使用 new 提供依赖,在更广阔的应用场景,Dagger2 还有妙招,暂且不提。

    再来查看生成的内容:

    import com.github.mrzhqiang.dagger2_example.account.Account;
    import dagger.internal.Factory;
    import dagger.internal.Preconditions;
    
    @SuppressWarnings({
        "unchecked",
        "rawtypes"
    })
    public final class AccountModule_ProvideAccountFactory implements Factory<Account> {
      private final AccountModule module;
    
      public AccountModule_ProvideAccountFactory(AccountModule module) {
        this.module = module;
      }
    
      @Override
      public Account get() {
        return provideAccount(module);
      }
    
      public static AccountModule_ProvideAccountFactory create(AccountModule module) {
        return new AccountModule_ProvideAccountFactory(module);
      }
    
      public static Account provideAccount(AccountModule instance) {
        return Preconditions.checkNotNull(instance.provideAccount(), "Cannot return null from a non-@Nullable @Provides method");
      }
    }
    

    本质上还是一个 Factory<T> 的实现,只不过依赖来自于账户模块。

    3.1.3 模块管理

    因为组件是 Dagger2 的核心,所以模块将交给 @Component 管理:

    @Component(modules = AccountModule.class)
    public interface ActivityComponent {
      void inject(MainActivity activity);
    }
    

    编译后的内容:

    import com.github.mrzhqiang.dagger2_example.MainActivity;
    import com.github.mrzhqiang.dagger2_example.MainActivity_MembersInjector;
    import dagger.internal.Preconditions;
    
    @SuppressWarnings({
        "unchecked",
        "rawtypes"
    })
    public final class DaggerActivityComponent implements ActivityComponent {
      private final AccountModule accountModule;
    
      private DaggerActivityComponent(AccountModule accountModuleParam) {
        this.accountModule = accountModuleParam;
      }
    
      public static Builder builder() {
        return new Builder();
      }
    
      public static ActivityComponent create() {
        return new Builder().build();
      }
    
      @Override
      public void inject(MainActivity activity) {
        injectMainActivity(activity);}
    
      private MainActivity injectMainActivity(MainActivity instance) {
        MainActivity_MembersInjector.injectAccount(instance, AccountModule_ProvideAccountFactory.provideAccount(accountModule));
        return instance;
      }
    
      public static final class Builder {
        private AccountModule accountModule;
    
        private Builder() {
        }
    
        public Builder accountModule(AccountModule accountModule) {
          this.accountModule = Preconditions.checkNotNull(accountModule);
          return this;
        }
    
        public ActivityComponent build() {
          if (accountModule == null) {
            this.accountModule = new AccountModule();
          }
          return new DaggerActivityComponent(accountModule);
        }
      }
    }
    

    之前的 new 实例代码不见了,转而用 提供依赖 的方式。

    思考:默认情况下,建造者模式会自动创建模块实例,如果从外部提供的话,将直接使用外部提供的模块实例。在这种情况下,模块是否可扩展将变得十分重要,这也是从一开始禁止模块扩展的原因。但我们也可以在不修改原有代码的前提下,进行依赖的迁移,前提是我们 面向接口编程

        public ActivityComponent build() {
          if (accountModule == null) {
            this.accountModule = new AccountModule();
          }
          return new DaggerActivityComponent(accountModule);
        }
    

    3.1.4 移除注入

    现在,我们试着移除构造函数上的 @Inject,再编译一下:

    消失的 Account 工厂类

    可以看到 Account 的工厂类已经消失,现在它是一个纯粹的 POJO 类。

    3.2 第三方依赖

    前面的内容不能完整描述提供第三方依赖的过程,所以我们可以再试一次。

    3.2.1 神奇的依赖库

    在项目的 build.gradle 文件中,引入 jitpack.io 依赖库:

    allprojects {
      repositories {
        google()
        jcenter()
        maven { url 'https://jitpack.io' }
      }
    }
    

    这是一个可以直接提供 Git 开源项目依赖的 maven 库,不需要经历漫长的 Maven 中央库审核、上传、同步以及发布的过程,在项目初期的快速开发阶段,它的帮助非常大。
    当然,如果你通过 nexus3 创建自己的 Maven 库,那就忽略我所说的这句话。

    3.2.2 添加依赖

    app 模块的 build.gradle 文件中,加入内容:

      implementation 'com.github.mrzhqiang:security:v1.0'
    

    说明一下,这个库是从 spring-security-crypto 扒到 Android 上来,用于信息的加密处理。

    为了运行这个库,我们还需要在 JDK1.8 版本上编译:

      compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
      }
    

    3.2.3 提供依赖

    好了,我们可以提供这个库的依赖给账户模块:

    import cn.mrzhqiang.security.crypto.factory.PasswordEncoderFactories;
    import cn.mrzhqiang.security.crypto.password.PasswordEncoder;
    import com.github.mrzhqiang.dagger2_example.account.Account;
    import dagger.Module;
    import dagger.Provides;
    
    @Module final class AccountModule {
      @Provides Account provideAccount() {
        return new Account();
      }
    
      @Provides PasswordEncoder providePasswordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
      }
    }
    

    编译后,在 di 包下生成一个新的提供者工厂:

    密码编码的提供者工厂

    3.2.4 注入依赖

    再注入到 MainActivity 并对密码进行加密处理:

    import android.os.Bundle;
    import android.widget.TextView;
    import androidx.appcompat.app.AppCompatActivity;
    import cn.mrzhqiang.security.crypto.password.PasswordEncoder;
    import com.github.mrzhqiang.dagger2_example.account.Account;
    import com.github.mrzhqiang.dagger2_example.di.DaggerActivityComponent;
    import javax.inject.Inject;
    
    public class MainActivity extends AppCompatActivity {
    
      @Inject Account account;
      @Inject PasswordEncoder passwordEncoder;
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerActivityComponent.create().inject(this);
    
        TextView contentText = findViewById(R.id.content_text);
        String content = String.format("username: %s, password: %s, encodePassword: %s",
            account.username,
            account.password,
            passwordEncoder.encode(account.password));
        contentText.setText(content);
      }
    }
    

    3.3 运行

    现在运行一下,看看效果:

    看到了吧,即使是 123456 这样的密码,经过加密处理后,也是普通暴力破解望而却步的长度和复杂度。

    当然,更说明依赖注入现在是正常生效,并且 @Provides 更符合日常开发使用。

    3.4 总结

    @Provides 把第三方库依赖接入 Dagger2 的管理中,并带来一个全新的概念——@Module 模块化。
    我们可以设计 NetworkModuleDatabaseModuleSecurityModule 等等模块,它们可以完全独立,也可以彼此关联。
    一旦模块分类完毕,后续的依赖管理就变得轻松简单。

    下一章,我们要实现组件和依赖的单例模式,以及如何巧妙避免注入时的重复代码。

    相关文章

      网友评论

          本文标题:Dagger2 | 三、进阶 - @Provides

          本文链接:https://www.haomeiwen.com/subject/cldgxktx.html