【问题标题】:Spring @Autowired violates DRY principle. What can be done?Spring @Autowired 违反了 DRY 原则。可以做什么?
【发布时间】:2018-04-22 10:05:18
【问题描述】:

我使用的代码中的一些控制器和服务有很多 @Autowired 依赖项。自动装配有两种流行的方法 - Autowire 字段和 Autowire 构造函数。 在第一种情况下,您复制注释 @Autowired

public class SomeController {
    @Autowired
    private SomeService1 someService1;
    @Autowired
    private SomeService2 someService2;
    @Autowired
    private SomeService3 someService3;
}

在第二种情况下,您创建了一个丑陋且无用的构造函数。

public class SomeController {
    private final SomeService1 someService1;
    private final SomeService2 someService2;
    private final SomeService3 someService3;

    @Autowired
    public SomeController(SomeService1 someService1,
                          SomeService2 someService2,
                          SomeService3 someService3) {
        this.someService1 = someService1;
        this.someService2 = someService2;
        this.someService3 = someService3;
    }
}

当您有很多应该自动装配的字段时,情况会变得更糟。我同意很多依赖项通常标志着一个糟糕的设计,但这不是我的问题。我想使用@AutowireAll 之类的东西来避免代码重复,该注释将自动装配所有可能自动装配的字段。我搜索了它,但找不到。可能的代码如下所示

@AutowireAll
public class SomeController {
    private SomeService1 someService1;
    private SomeService2 someService2;
    private SomeService3 someService3;
}

如何避免代码重复?

【问题讨论】:

  • 这会让你遇到 Jigsaw 对反射的限制(至少,只要你不禁用它们)。在我看来,“丑陋的构造函数”是更清洁的解决方案。
  • 不,我不认为这是可能的。 @Autowired 在您的领域没有违反 DRY。它相当清楚地定义了您要为 DI 考虑哪些字段。例如,您不会说使用公共/私有访问修饰符违反了 DRY。顺便说一句,字段注入是一种不好的做法,建议使用构造函数注入。同意长构造函数问题,但也正如您所说,这是代码的味道。
  • 不知道 SO 上禁止使用词库。我们都使用它们。使用库而不发明轮子并不丢人。
  • 如果有人感兴趣,有一个使用 Lombok AllArgsConstructor 的解决方案。所以你不需要构造函数,也不需要@Autowired 注释,只需要字段,它们可以是最终的。
  • 并不是说 Word 库是题外话——我们所有人一直都在使用库,适用​​于所有编程语言。但是,根据help center,询问 what 库使用“对于 Stack Overflow 来说是无关紧要的,因为它们往往会吸引固执己见的答案和垃圾邮件”。

标签: java spring spring-boot dry


【解决方案1】:

使用Lombok注解@AllArgsConstructor可以避免代码重复

@AllArgsConstructor
public class SomeController {
    private final SomeService1 someService1;
    private final SomeService2 someService2;
    private final SomeService3 someService3;
}

它在Spring/Spring Boot 版本中工作,在唯一的构造函数上不需要@Autowired。这些版本是 Spring Boot 1.4+、Spring 4.3+

@AllArgsConstructor 创建一个可能需要的构造函数(请参阅他的回答下的 Karol Dowbecki cmets)。

缺点是要使用 Lombok,您需要一个 IDE 插件。 Eclipse 和 IDEA 都有这样的插件,但由于您不是唯一一个从事项目的开发人员,所有其他开发人员也需要它。

【讨论】:

    【解决方案2】:

    如果只有一个构造函数,则不必使用@Autowired。默认情况下会自动连接:

    @Controller
    public class SomeController {
      private final SomeService1 someService1;
      private final SomeService2 someService2;
    
      public SomeController(SomeService1 someService1, SomeService2 someService2) {
        this.someService1 = someService1;
        this.someService2 = someService2;
      }
    }
    

    根据docs, chapter 1.9.2. @Autowired

    从 Spring Framework 4.3 开始,@Autowired 注释在这样的 如果目标 bean 只定义了一个,则不再需要构造函数 构造函数开始。但是,如果有多个构造函数 可用,必须至少注释一个以教导容器 一个可以使用。

    【讨论】:

    • 但正如我所说,我不希望有一个带有一长串参数的构造函数。
    • 您将如何确保注入所需的依赖项,@PostConstruct?您将如何通过反射或访问字段对 bean 进行单元测试?构造函数有其用处。
    • 为什么要确保注入所需的依赖项。它违反了不测试框架原则。如果没有注入某些依赖项,无论如何我都会看到 NPE,但我不想污染代码以便更早地看到它(尽管我同意 Fail Fast 意识形态)。另外,如果我应该选择一个,我更喜欢脏测试而不是脏代码。
    • 这就像在没有参考实际输入的情况下创建Scanner,这是没有意义的。允许创建和使用无效对象的代码更难理解,而且绝对不干净。 Spring Framework 有这方面的一章,找“Constructor-based or setter-based DI?”在文档中。对于您的@Controller,它归结为“Spring 团队通常提倡构造函数注入,因为它使人们能够将应用程序组件实现为不可变对象并确保所需的依赖项不为空。”
    猜你喜欢
    • 2014-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-29
    相关资源
    最近更新 更多