Dagger: 在提供的POJO上注入字段

4

目前正在使用Dagger进行测试,我想要的是实例化和注入不同的Bar实现。如何在提供的字段中注入字段?例如:

模块:

@Module(
        injects = {
                Main.class
        },
        complete = false,
        library = true
)
public class ExampleTestModule {
    @Provides
    public Foo providesFoo() {
        return new Foo();
    }
    @Provides
    public Bar providesBar(BarImpl impl) {
        // return new BarImpl(); // null
        return impl;
    }
}

主要:

public class Main {
    @Inject
    Foo foo;
}

Foo:

public class Foo {
    @Inject
    Bar bar;
}

酒吧:

public interface Bar {

}

BarImpl

public class BarImpl implements Bar {
}

测试用例:

public class ApplicationTest extends ApplicationTestCase<Application> {
    public ApplicationTest() {
        super(Application.class);
    }


    public void testFoo() {
        Main main = new Main();
        ObjectGraph.create(new ExampleTestModule()).inject(main);
        assertNotNull(main.foo);
    }

    public void testFooBar() {
        Main main = new Main();
        ObjectGraph.create(new ExampleTestModule()).inject(main);
        assertNotNull(main.foo.bar);
    }
}

Main.Foo不为空,但Main.Foo.Bar为空。

1个回答

9
您从未将bar注入到foo中。
ObjectGraph.create(new ExampleTestModule()).inject(main);

这行代码仅查看被@Inject注释的main字段,并注入它们。没有递归行为。


修复问题

让我们一步一步来:

  • You provided complete = false and library = true in your Module. You should only use these if really necessary. Dagger will give you warnings when something is wrong, and these properties surpress these warnings. For example, removing them raises the following warning when compiling:

    Error:(11, 8) error: No injectable members on BarImpl. Do you want to add an injectable constructor? required by providesBar(BarImpl) for ExampleTestModule.
    
  • Let's add an empty injectable constructor to BarImpl, as it suggests:

    public class BarImpl implements Bar {
        @Inject
        BarImpl(){
        }
    }
    
  • Compiling will give a new error:

    Error:(11, 8) error: Graph validation failed: You have these unused @Provider methods:
    1. ExampleTestModule.providesBar()
    Set library=true in your module to disable this check.
    
  • Apparently, providesBar() is never used. That means, the bar field in Foo will never be injected. You can do two things:

    1. Inject bar manually:

      ObjectGraph graph = ObjectGraph.create(new ExampleTestModule());
      graph.inject(main);
      graph.inject(main.foo);
      
    2. Use injectable constructors (Preferred option):

      public class Foo {
          Bar bar;
      
          @Inject
          Foo(Bar bar){
              this.bar = bar;
          }
      }
      
  • Using the injectable constructor, you will now have a compile error in providesFoo(), since you don't supply a Bar instance in the Foo constructor. The nice thing about Dagger is, you can safely completely remove this method. Since Foo is annotated with @Injectable, everywhere it needs to inject a Foo instance, it uses this constructor. And when it uses this constructor, it notices it needs a Bar instance, and injects this as well.

  • Finally, we can remove the @Inject annotation from the Foo field in Main, and create an injectable constructor. Using ObjectGraph.get(Class<?>) we can retrieve a fully instantiated Main instance.

结果

最终的结果应该像这样:

模块:

@Module(
        injects = Main.class
)
public class ExampleTestModule {
    @Provides
    public Bar providesBar(BarImpl impl) {
        return impl;
    }
}

主要内容:

public class Main {
    Foo foo;

    @Inject
    Main(Foo foo) {
        this.foo = foo;
    }
}

Foo:

public class Foo {
    Bar bar;

    @Inject
    Foo(Bar bar){
        this.bar = bar;
    }
}

栏:

public interface Bar {
}

BarImpl:

public class BarImpl implements Bar {
    @Inject
    BarImpl(){
    }
}

ApplicationTest:

public class ApplicationTest extends ApplicationTestCase<Application> {

    public ApplicationTest() {
        super(Application.class);
    }


    public void testFoo() {
        Main main = ObjectGraph.create(new ExampleTestModule()).get(Main.class);
        assertNotNull(main.foo);
    }

    public void testFooBar() {
        Main main = ObjectGraph.create(new ExampleTestModule()).get(Main.class);
        assertNotNull(main.foo.bar);
    }
}

结论

从结果中,我们可以得出一些结论:

  • 不要仅仅将library = truecomplete = false添加到您的模块中。这仅在使用多个复杂模块时才是必需的。
  • 尝试使用可注入构造函数。这是Dagger的设计初衷,并且效果最佳。额外的好处是现在你可以像应该的那样把字段设为private
  • 当使用可注入构造函数时,只有在注入接口实例时才需要创建providesXXX方法,就像我们使用BarBarImpl一样。因为,嘿,这正是依赖注入的目的,对吧?

我需要在无参数构造函数上添加@Inject注释吗?就像在BarImpl上一样。另外,providesBar返回参数和返回新创建的Bar实例的providesBar之间有什么区别? - Cypher Text
  1. 问题在于Dagger需要在构造函数上使用@Inject注释。由于您没有任何特殊的构造函数,因此这将是无参数构造函数。
  2. 当使用BarImpl参数时,您可以让Dagger为您创建一个实例。如果BarImpl具有任何可注入字段,则会自动完成此操作(就像在Foo类中一样)。在这种情况下,您可以轻松地自己返回一个新的BarImpl实例。
- nhaarman
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - nhaarman
2
这个注释应该在Dagger页面的第一页。 - bajicdusko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接