英文:
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 "main" 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("in C log"); //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<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();
}
}
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;
}
}
现在,您可以在任何地方注入B
或Provider<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("in C log");
}
}
If even this is more than you want to change (or can change) in B
, read on.
Option 2: Inject a MembersInjector<B>
Dagger contains the interface MembersInjector
, which does exactly what it sounds like. You can inject MembersInjector<B>
into A
, and use that to construct B
in two steps.
@Inject
A(MembersInjector<B> 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<B> injector) {
B b = new B();
injector.injectMembers(b);
return b;
}
}
Now you can inject B
or Provider<B>
anywhere, without modifying B
at all.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论