可变性和Spring

23

我正在阅读《Effective Java》一书,同时在一家重度使用Spring Dependency Injection的商店工作。在阅读Bloch的书时,人们不禁会注意到他在类的不可变性方面所强调的重要性(他多次表明类应该尽可能不可变)。我无法避免地感到这与Spring Dependency Injection(以及大多数DI引擎)对javabeans标准的依赖形成了直接冲突。

读《Spring in Action》中关于DI的章节时,看起来会让Bloch感到不舒服,因为它们由在您的视野之外实例化的对象组成的可变类可能本身是可变的。

是否是Bloch的思想对于Spring来说太新颖了?Spring模型失灵了吗?Bloch对于不可变性的立场只适用于编写库代码吗?在编写Spring代码时,我应该编写具有许多getter和setter的灵活对象或者将所有东西都加载到构造函数中呢?


3
Bloch并没有说“永远不要让事物变得可变”,他说“不要让它们比必要更加可变”。如果您的框架需要一定程度的可变性,那么就必须接受这一点。 - skaffman
1
在涉及可变性、望远构造函数和构建器模式的项目中,他有点儿贬低了JavaBeans标准(称其为过时),但是这个标准是Spring的基础,这是否意味着Spring也过时了? - nsfyn55
Spring框架不依赖于JavaBeans标准。 - Bozho
2
很抱歉给您发了那个链接,请在该链接的第4页第3段找到以下引用:“Spring使用JavaBeans规范来构建其DI配置模型的核心”。 - nsfyn55
1
我编辑了上面的帖子。在我放置MVC的所有地方,我都是指DI。 - nsfyn55
你说得完全正确。你可以使用构造函数注入,但会带来两个问题:1)丑陋的长构造函数,2)无法使用循环依赖。解决这些问题实际上是相当好的。强制不可变性最终将强制执行其他良好的实践。 - rdllopes
3个回答

12

实际上,Spring Bean 的设计理念是不可变的,即使你没有强制执行。

你可以为通过构造函数注入初始化的 final 字段提供一个 getter 方法。

通常情况下,你不这样做,但是你永远不应该重新分配由 DI 框架注入的 Bean 的字段。这是因为 Spring Bean 通常不保存除它们的依赖之外的任何状态(它们的作用域是单例)。当然,也有例外,比如原型和请求范围的 Bean,但这些很少见(例如,在我使用的两个大项目和两个中等项目中,只有一个原型范围的 Bean)。


3
我同意:大多数由Spring实例化的对象都是“单例”的,而且并不真正可变(例如服务、数据访问对象、控制器、事务管理器等)。 - Tristan
@nsfyn55 完全由您决定注入什么对象。这与 DI 没有任何关系。 - Bozho
@nsfyn55 在你所谈论的层面上,Spring 没有任何作用 - 答案完全取决于你如何设计你的代码和接口。 - matt b
@Bozho - 我不认为你上面所说的完全准确。Spring Beans 可以保存任意多或少的状态。如果你遵循 Spring 推荐的做法,使用接口来设置属性,那么就没有什么可以阻止你获取可变对象的引用,也没有办法知道哪些其他类持有对该对象的引用。 - nsfyn55
1
对于单例bean,你不会存储状态。而这是大多数情况。 - Bozho
显示剩余6条评论

10

如果您使用基于构造函数的注入方式,即使将类设置为不可变,仍然可以使用依赖注入。这样,您就可以避免使用不必要的setter方法。


2

我个人认为并没有冲突,特别是在使用Spring MVC时。哪些bean由Spring管理?大多数情况下是你的控制器以及在服务/数据层中的DAO和服务。这些通常都没有真正的状态,也没有setter。如果你的问题在于setter注入(例如,你有自己的类需要由Spring管理,但你不想为某些字段设置setter),那么你可以使用构造函数注入(或者两者结合起来)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接