为何 Spring 的 ApplicationContext.getBean 被认为不佳?
为何 Spring 的 ApplicationContext.getBean 被认为不佳?
技术背景
在 Spring 框架中,ApplicationContext.getBean()
是用于从 Spring 应用上下文中获取 Bean 的方法。然而,许多开发者建议尽量避免使用该方法。Spring 的核心特性之一是依赖注入(Dependency Injection,DI),也称为控制反转(Inversion of Control,IoC),其目的是让类无需关心如何获取依赖对象,从而提高代码的可维护性、可测试性和可扩展性。
避免使用 ApplicationContext.getBean()
的原因
违背控制反转原则
控制反转的理念是让类不了解也不关心如何获取其依赖的对象。而调用 ApplicationContext.getBean()
并非控制反转,因为类直接依赖于 Spring 来提供依赖,无法通过其他方式获取。例如:
1 |
|
在这种情况下,类与 Spring 框架紧密耦合,无法在测试时轻松提供模拟实现。
降低代码可测试性
使用 ApplicationContext.getBean()
会使测试变得复杂。为了测试使用该方法的类,需要模拟 ApplicationContext
,这是一个庞大的接口,增加了测试的难度。以下是一个示例:
1 |
|
测试这个类时,需要使用 Mockito 等框架来模拟 ApplicationContext
:
1 |
|
相比之下,使用依赖注入的方式进行测试会更简单:
1 |
|
增加代码复杂度
使用 ApplicationContext.getBean()
会使代码变得复杂,因为需要显式地从应用上下文中获取 Bean。而且,如果 Bean 的名称发生变化,需要在多个地方修改代码。
不利于代码维护
对于维护人员来说,理解使用 ApplicationContext.getBean()
的代码会更加困难。他们需要了解 Spring 配置和所有相关的位置,才能弄清楚对象是如何连接的。
替代方案
依赖注入
通过构造函数或 setter 方法进行依赖注入是更好的选择。例如:
1 |
|
在 Spring 配置文件中进行配置:
1 |
|
Spring 会自动将 myClass
注入到 myOtherClass
中。
使用注解
使用 @Autowired
注解可以更方便地实现依赖注入:
1 |
|
最佳实践
- 尽量使用依赖注入来管理对象之间的依赖关系,避免直接使用
ApplicationContext.getBean()
。 - 只有在必要时,如在应用启动时获取根 Bean,才使用
ApplicationContext.getBean()
。 - 在测试时,优先选择使用依赖注入的方式,这样可以更轻松地提供模拟对象。
常见问题
是否绝对不能使用 ApplicationContext.getBean()
?
不是的。在某些情况下,如应用启动时获取根 Bean,或者在需要根据用户交互动态创建 Bean 实例时,可以使用 ApplicationContext.getBean()
。但应该尽量减少其使用频率。
使用 @Autowired
和 ApplicationContext.getBean()
有什么区别?
@Autowired
是 Spring 提供的依赖注入注解,通过它可以让 Spring 自动将依赖的 Bean 注入到类中,代码与 Spring 的耦合度相对较低。而 ApplicationContext.getBean()
是直接从应用上下文中获取 Bean,代码与 Spring 框架紧密耦合。
如何在不使用 ApplicationContext.getBean()
的情况下测试 Spring 管理的类?
可以使用 Spring 的测试框架,如 @SpringBootTest
,结合 @MockBean
注解来创建模拟对象,进行单元测试或集成测试。也可以使用依赖注入的方式,在测试时手动提供模拟对象。