Dagger2:如何在不注入其组件类的情况下注入字段?

huangapple go评论61阅读模式
英文:

Dagger2: How to inject field without injection of it's compositor class?

问题

我的A类需要对C类进行依赖注入。
C类是B类的组件(B类具有C作为字段)。
A类能否在不注入B的情况下注入C?

[A已注入] ->(B未注入到A) -> [C已注入到B]

在我的实际项目中,我需要将字段注入到一个类中,但不需要将注入队列添加到包含(直接或间接)此字段的所有类中。

目前我的B类中出现异常:

Exception in thread "main" java.lang.NullPointerException
        at B.log(B.java:28)
        at A.b_foo(A.java:28)
        at App.main(App.java:36)

请给予建议。
代码如下:

App.java

import dagger.Component;
import javax.inject.Singleton;
 
public class App {
  @Singleton
  @Component(
      modules = {
              CModule.class
             }
  )
  public interface AppRunner {
    A instance();
  }

  public static void main(String[] args) {
    AppRunner appRunner = DaggerApp_AppRunner.builder().build();
    appRunner.instance().foo();
  }
}

A.java

import javax.inject.Inject;

public class A {
    private final B b;

    @Inject
    A() {
        this.b = new B();
    }

    public void foo() {
        b.log();
    }
}

B.java

import javax.inject.Inject;

public class B {

  @Inject
  C c;

  B() { }

  public void log() {
      c.log("在C日志中"); //此处发生java.lang.NullPointerException异常,因为C在B之前没有被注入
  }
}

C.java

import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.ArrayList;
import java.util.List;

@Singleton
public final class C {
  private final List<String> logs = new ArrayList<>();

  @Inject
  C() {}

  public void log(String msg) {
    logs.add(msg);
  }
}

CModule.java

import dagger.Module;
import dagger.Provides;

@Module
public abstract class CModule {
    @Provides
  static C provideC() {
        return new C();
    }
}

更新:我能否拥有完全工作的代码而不需要B的注入?我的A类知道Dagger的存在。但是A类不能具有B的注入。

英文:

My Class A needs Dependency injection of Class C.
C class is component of B class (B has C as a field).
Can Class A inject C without B injection?

[A injected]->(B not injected to A)->[C injected to B]

I my real project I need to inject field to one class but don't need adding injection queue to the all classes contains (directly or indirectly) this field.

Currently I have Exception in B class:

Exception in thread &quot;main&quot; java.lang.NullPointerException
        at B.log(B.java:28)
        at A.b_foo(A.java:28)
        at App.main(App.java:36)

Please advice.
Code:

App.java

import dagger.Component;
import javax.inject.Singleton;
 
public class App {
  @Singleton
  @Component(
      modules = {
              CModule.class
             }
  )
  public interface AppRunner {
    A instance();
  }

  public static void main(String[] args) {
    AppRunner appRunner = DaggerApp_AppRunner.builder().build();
    appRunner.instance().foo();
  }
}

A.java

import javax.inject.Inject;

public class A {
    private final B b;

    @Inject
    A() {
        this.b = new B();
    }

    public void foo() {
        b.log();
    }
}

B.java

import javax.inject.Inject;

public class B {

  @Inject
  C c;

  B() { }

  public void log() {
      c.log(&quot;in C log&quot;); //java.lang.NullPointerException here because B was not injected before C
  }
}

C.java

import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.ArrayList;
import java.util.List;

@Singleton
public final class C {
  private final List&lt;String&gt; logs = new ArrayList&lt;&gt;();

  @Inject
  C() {}

  public void log(String msg) {
    logs.add(msg);
  }
}

CModule.java

import dagger.Module;
import dagger.Provides;

@Module
public abstract class CModule {
    @Provides
  static C provideC() {
        return new C();
    }
}

Update: Can I have fully working code without B injection at all? My A class knows about Dagger. But A must don't have B injection.

答案1

得分: 2

选项1:只需将@Inject添加到构造函数

就是这样。您不需要将依赖项移到构造函数中。

public class B {

  @Inject
  C c;

  @Inject // 这个注解是唯一添加的内容。
  B() { }

  public void log() {
      c.log("in C log");
  }
}

如果即使这一步对于您要在B中进行的更改来说都太多(或者无法进行更改),请继续阅读。

选项2:注入MembersInjector<B>

Dagger包含了接口MembersInjector,其功能与名称相符。您可以将MembersInjector<B>注入到A中,并在两个步骤中使用它来构造B

@Inject
A(MembersInjector<B> injector) {
    this.b = new B();
    injector.injectMembers(b);
}

由于MembersInjector是Dagger的一部分而不是JSR-330,这意味着类A必然了解Dagger。这可能是个问题,但您可以轻松地将此逻辑移出A

选项3:在@Provides方法中使用MembersInjector

@Module
class BModule {
    @Provides
    static B provideB(MembersInjector<B> injector) {
        B b = new B();
        injector.injectMembers(b);
        return b;
    }
}

现在,您可以在任何地方注入BProvider<B>,而无需对B进行任何修改。

英文:

You have an @Inject field in class B, and you want to be able to get a fully initialized B within A

Option 1: Just add @Inject to the constructor

That's it. You don't need to move the dependencies into the constructor.

public class B {

  @Inject
  C c;

  @Inject // This annotation is the only thing added.
  B() { }

  public void log() {
      c.log(&quot;in C log&quot;);
  }
}

If even this is more than you want to change (or can change) in B, read on.

Option 2: Inject a MembersInjector&lt;B&gt;

Dagger contains the interface MembersInjector, which does exactly what it sounds like. You can inject MembersInjector&lt;B&gt; into A, and use that to construct B in two steps.

@Inject
A(MembersInjector&lt;B&gt; injector) {
    this.b = new B();
    injector.injectMembers(b);
}

Since MembersInjector is part of Dagger and not JSR-330, this means that class A necessarily has knowledge about Dagger. This is probably an issue, but you can easily move this logic out of A:

Option 3: Use MembersInjector in a @Provides method.

@Module
class BModule {
    @Provides
    static B provideB(MembersInjector&lt;B&gt; injector) {
        B b = new B();
        injector.injectMembers(b);
        return b;
    }
}

Now you can inject B or Provider&lt;B&gt; anywhere, without modifying B at all.

huangapple
  • 本文由 发表于 2020年9月10日 17:12:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/63826481.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定