【问题标题】:Spring Dependency Injection - Private fields - Anti Pattern? Why does it even work?Spring 依赖注入 - 私有字段 - 反模式?为什么它甚至可以工作?
【发布时间】:2020-08-26 15:12:45
【问题描述】:

我通常是一名 c# 开发人员,但时不时地在 Java 上工作,然后我看到很多使用 Spring 在 private 属性上的依赖注入,没有设置值的公共方式。我很惊讶这确实有效,但我想这可能是通过反射实现的?

这肯定是可怕的做法?!我看不出任何单元测试或检查类的人怎么可能知道需要从某个外部框架设置私有成员。

当您进行单元测试时,您甚至会如何设置属性?还是单独使用类?

我猜你必须在你的单元测试中使用 spring,这看起来真的有点矫枉过正。当然,您应该能够在没有 IOC 容器的情况下进行单元测试吗?类变得完全依赖于spring...

我错过了什么吗?

依赖注入是否应该不总是涉及某种公共设置器,如果可能的话最好使用构造函数?还是我缺少关于 Java 的一些东西...?

谢谢

【问题讨论】:

    标签: java spring dependency-injection inversion-of-control ioc-container


    【解决方案1】:

    即使您有私有字段,您也可以随时模拟注入的 bean。您应该查看 Spring 文档中的 @MockBean。本质上,您可以执行以下操作:

    @ExtendWith({SpringExtension.class})
    class MyServiceTest{
    
        @MockBean
        private RepositoryInterface repository;
    
        @Autowired
        private MyService service;
    
    }
    

    假设RepositoryInterface 是注入MyService 的接口(而不是具体类)。发生的情况是 JUnit5 的 SpringExtension,如果您从 Spring Initialzr 创建 pom.xml,它应该已经在您的依赖项中,它将使用另一个名为 Mockito 的框架为该接口构建一个模拟(也许看看它)。然后 Spring IoC 将在服务中注入创建的模拟。这适用于现场注入:

    @Service
    public class MyService{
    
        @Autowired
        private RepositoryInterface repositoryInterface
    }
    

    setter 注入:

    @Service
    public class MyService{
    
        private RepositoryInterface repositoryInterface
    
        @Autowired
        public void setRepository(RepositoryInterface repositoryInterface){
            this.repositoryInterface = repositoryInterface;
        }
    }
    

    或构造函数注入:

    @Service
    public class MyService{
    
        private RepositoryInterface repositoryInterface
    
        public MyService(RepositoryInterface repositoryInterface){
            this.repositoryInterface = repositoryInterface;
        }
    }
    

    本质上,最后一个是推荐的,因为这样您的服务的依赖关系将是明确的。更多的是代码风格。不建议使用字段注入,因为我会隐藏您的类依赖项。因此,使用构造函数注入构建测试的推荐方法如下:

    @ExtendWith({SpringExtension.class})
    class MyServiceTest{
    
        @MockBean
        private RepositoryInterface repository;
    
        private MyService service;
    
        @BeforeEach
        void setup(){
            service = new MyService(repository);
        }
    }
    

    希望这有助于您的理解。

    【讨论】:

    • 好吧,我还是很疑惑spring是如何设置私有字段的。我猜在Java中你可以用反射来做到这一点?虽然不推荐,但我仍然不完全理解为什么有人会考虑这个,我已经在很多代码中看到了它。无论如何,感谢您对春季 DI 的有力总结。
    • 是的,它是使用反射完成的。如果您进行一些搜索,您会发现许多通过反射将私有字段可见性强制为非私有的方法。显然,这是一件极其有害的事情。我认为您会发现很多代码由于“懒惰”而使用字段注入......前段时间字段注入是一种标准的东西,所以人们仍然在进行这种不良行为。
    【解决方案2】:

    是的,它有效。一些测试框架允许注入私有字段。

    是的,它是反模式,增加了技术债务 - 易于编写,但难以维护此类代码,而不是编译时错误,您将遇到运行时错误。不要那样做。使用构造函数注入。

    【讨论】:

      【解决方案3】:

      有基于字段的注入、基于setter的注入、基于注解的注入和基于构造函数的注入。基于构造函数的注入最适合测试,因为您可以轻松地模拟所需的依赖项。尽可能用 final 字段定义服务总是很好的。

      class MyService {
      
          private final MyDependency dependency;
      
          @Autowired // not needed, explicit just for this example
          public MyService(MyDependency dependency) {
              this.dependency = dependency;
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-07-07
        • 1970-01-01
        • 2020-10-13
        • 2016-12-07
        • 1970-01-01
        • 1970-01-01
        • 2012-11-14
        • 2011-07-22
        相关资源
        最近更新 更多