Spring 依赖注入 - 私有字段 - 反模式?为什么它甚至可以工作?

Spring Dependency Injection - Private fields - Anti Pattern? Why does it even work?

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

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

在进行单元测试时,您甚至如何设置 属性?或者只是单独使用 class?

我猜你必须在你的单元测试中使用 spring,这似乎有点过分了。当然,您应该能够在没有 IOC 容器的情况下进行单元测试? class 变得完全依赖于 spring...

我是不是漏掉了什么?

依赖注入不应该总是涉及某种public setter,并且如果可能最好使用构造函数吗?或者有什么关于 Java 我想念的......?

谢谢

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

class MyService {

    private final MyDependency dependency;

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

}

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

@ExtendWith({SpringExtension.class})
class MyServiceTest{

    @MockBean
    private RepositoryInterface repository;

    @Autowired
    private MyService service;

}

假设 RepositoryInterface 是在 MyService 中注入的接口(而不是具体的 class)。如果你从 Spring Initialzr 创建你的 pom.xml,那么 JUnit5 的 SpringExtension 应该已经在你的依赖项中,将会使用另一个框架为该接口构建一个模拟,该框架被称为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;
    }
}

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

@ExtendWith({SpringExtension.class})
class MyServiceTest{

    @MockBean
    private RepositoryInterface repository;

    private MyService service;

    @BeforeEach
    void setup(){
        service = new MyService(repository);
    }
}

希望这有助于您的理解。

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

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