我如何在 Dagger 中“提供”一个空方法

why*_*yem 2 java dependency-injection dagger-2

我正在尝试从 Spring 迁移一个项目,并按照示例 GitHub 存储库开始使用 Dagger 2。

在 Spring 中,我可以在类级别使用注释来公开类的所有方法,包括void方法。我想在 Dagger 2 中做到这一点,即,我想“提供”一种void方法。

在下面的代码示例中,也许我应该将printRandomUUID方法移到Printer接口中。但是,我正在做这个小练习,目的是迁移经典的 Spring@Component@Service.

正确的做法是什么?是否可以在组件或模块级别提供 void 方法?

public class Main {

 interface Printer {

    void printMsg(String msg);
 }

 static class ConsolePrinter implements Printer {

    @Override
    public void printMsg(String msg) {
        System.out.println(msg);
    }
 }

 @Singleton
 @Component(modules = ConsoleModule.class)
 interface HelloWorldApp {

    Printer getPrinter();

    //this doesn't compile -> java.lang.IllegalArgumentException: not a valid component method:
    void printRandomUUID();
 }

 @Module
 static class ConsoleModule {

    @Provides
    Printer providePrinter() {
        return new ConsolePrinter();
    }

    //this doesn't compile -> @Provides methods must return a value (not void)
    @Provides
    void printRandomUUID() {
        System.out.println(UUID.randomUUID().toString());
    }

 }

 public static void main(String[] args) {
    HelloWorldApp app = DaggerMain_HelloWorldApp.create();
    app.getPrinter().printMsg("Hello");
    System.out.println("-");
    //app.printRandomUUID();//here i want a void method to be consumed, from the component.
 }
}
Run Code Online (Sandbox Code Playgroud)

Jef*_*ica 5

这是不可能的(还)。与 Spring 不同,Dagger 仅通过检查组件接口和模块来配置。这意味着 Dagger@Component方法必须@Component文档中方法的格式相匹配,并且目前无法提供委托给其他实例的任意代码或方法。

这并不是说组件不能有 void 方法:它们可以,但它们必须是单参数方法,@Inject在外部创建的实例中注入带注释的方法和字段。这些被称为成员注入方法,而不是void它们也可以返回它们接受的类型以便于链接。

从不同的角度来看,出于简单性和正确性的原因,我认为将任意业务逻辑与 Dagger 创建的组件相结合是一个坏主意:

  • 这样做可能会违反 SRP 或关注点分离:依赖注入的优势之一是将对象创建逻辑与其他业务逻辑分离。允许在对象创建组件上添加业务方法应该与new在业务组件中使用一样不合适。(是否应该通过 DI 图提供每个对象是另一天有争议的话题。)
  • 如果您坚持最佳实践并避免在构造函数/工厂/提供程序中产生副作用和其他“繁重的工作”,那么您应该能够清楚地推断在 Component 方法中什么可以发生,什么不可以发生。允许在组件上使用任意方法——尤其是无效方法——将与这种做法背道而驰。
  • 如果您的应用程序使用单独的粒度库而不是单一的编译步骤,那么从它自己的对象图中使用一个组件可能会很难在不引入依赖循环的情况下进行构建。当然,Dagger 确实允许在其自己的图形中注入组件,但鲁莽地这样做可能会导致以后出现循环问题。
  • 类似的用例很容易使用现有的结构来表示——正如 Louis Wasserman 评论的那样,通过图形可以使用 Runnable,或者注入一个类似的单一用途对象来保存方法——保持组件的任意实现似乎是不会导致功能或可读性的大损失。最坏的情况是,您需要一个额外的方法调用来访问您定义的类。

如果我像您一样迁移,我会在 HelloWorldApp 旁边创建一个名为 HelloWorldMethods 的类,并将我将放在 HelloWorldApp 上的所有方法转移到该类上。如果这是 Spring 迁移中的常见模式,您甚至可以为它定义一个本地约定(例如,FooComponent 带有 FooMethods 或 FooUtil)。最后,如果您想隐藏 Dagger 实现细节(如在外部 API 中),您还可以编写自己的类来包装和使用您的组件,将重要方法委托给内部组件并提供您需要的任意实现。