【问题标题】:Can a Dagger 2 dependency be non-injectable?Dagger 2 依赖项可以是不可注入的吗?
【发布时间】:2016-08-30 05:19:24
【问题描述】:

有没有办法告诉 Dagger 2 如何提供一些东西,但不允许它被注入?

假设我想注入一个QuxQux 需要 FooBar

@Provides
@Singleton
Foo foo() {
    return new Foo();
}

@Provides
@Singleton
Bar bar() {
    return new Bar();
}

@Provides
@Singleton
Qux qux(final Foo foo, final Bar bar) {
    return new Qux(foo, bar);
}

但是,如果我不希望 FooBar 可注射怎么办?也许暴露它们会破坏Qux 的封装,或者它们是某种我只希望Qux 能够访问的工厂。

我已经想到了几种方法可以实现这一目标:

  • 如果其他提供者需要Foo 单例,我可以将其设为类成员。但如果 Foo 有自己的多个依赖项,这将变得一团糟。
  • 如果任何其他提供者都不需要 Bar 单例,我可以在 Qux 提供者中创建实例。但是如果Qux 有几个这样的依赖项,这会变得很混乱。

这些解决方案看起来都不是很优雅也不是很狡猾。还有其他方法吗?

【问题讨论】:

    标签: dagger-2


    【解决方案1】:

    实现您想要做的最简单的方法是定义一个包私有限定符。

    package my.pkg;
    
    @Qualifier
    @Retention(RUNTIME)
    @interface ForMyPackage {}
    

    然后,将其用于 Foo 和 Bar 的绑定:

    @Provides
    @Singleton
    @ForMyPackage
    Foo foo() {
        return new Foo();
    }
    
    @Provides
    @Singleton
    @ForMyPackage
    Bar bar() {
        return new Bar();
    }
    
    @Provides
    @Singleton
    Qux qux(final @ForMyPackage Foo foo, final @ForMyPackage Bar bar) {
        return new Qux(foo, bar);
    }
    

    这样,如果您有权访问限定符,则只能请求注入这些版本的 Foo 和 Bar。

    如果所有这些绑定都在一个模块中,您甚至可以在模块中使用私有的嵌套限定符。

    由提问者编辑:

    我尝试了最后一个建议。

    @Qualifier
    @Retention(RUNTIME)
    private @interface NoInject {}
    
    @Provides
    @Singleton
    @NoInject
    Foo foo() { return new Foo(); }
    

    根据需要尝试注入 Foo 会导致编译时错误:

    Error:(15, 6) Gradle: error: com.mydomain.myapp.Foo 不能在没有 @Inject 构造函数或 @Provides- 或 @Produces-annotated 方法的情况下提供。 com.mydomain.myapp.MyActivity.foo [注入字段类型:com.mydomain.myapp.Foo foo]

    因此,虽然错误消息有点误导,但这种技术是简洁有效的。

    【讨论】:

    • 是的,如果 FooBar 的绑定与 Qux 的绑定在不同的包中,那么这将不起作用,但我认为如果你想要这种关系,它有点奇怪,它会跨越包边界。
    【解决方案2】:

    我想说这听起来很像component dependencies

    [..] 作为替代方案,组件只能通过声明组件依赖项来使用来自另一个组件接口的绑定。当一个类型被用作组件依赖时,依赖的每个提供方法都被绑定为提供者。请注意,只有作为配置方法公开的绑定才能通过组件依赖项获得。

    如果您真的想隐藏您的FooBar,您可以将模块、FooBar 包本地化。然后创建一个只暴露Qux的组件,并将其用作组件依赖。

    由于FooBar 是本地包,SubComponent 也不应该能够使用它们。
    您的组件将是唯一能够使用该模块的组件,因此在其依赖图中创建和使用 FooBar

    【讨论】:

      【解决方案3】:

      在别处转发我的答案...

      一个简单的方法是:

      @Retention(BINARY)
      @Qualifier
      private annotation class InternalApi
      
      @Module
      object NetworkModule {
        @Provides 
        @InternalApi 
        fun provideClient(): OkHttpClient {
          //...
        }
      
        @Provides
        fun provideRetrofit(
          @InternalApi client: Lazy<OkHttpClient>
        ): Retrofit {
          //...
        }
      }
      

      选自here

      基本上,我们创建私有限定符——在这种情况下,@InternalApi 用于限定 OkHttpClient 依赖项,从而使其对我们的模块私有。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-28
        • 1970-01-01
        • 1970-01-01
        • 2017-03-17
        • 1970-01-01
        • 2018-03-09
        相关资源
        最近更新 更多