【问题标题】:Why does Spring dependency injection have init-method?为什么Spring依赖注入有init-method?
【发布时间】:2012-07-07 23:54:14
【问题描述】:

我来自.NET背景,使用过Unity、Ninject、Castle Windsor等依赖注入容器。最近开始学习使用Spring对Java的依赖注入能力。

在学习 Spring 时,我发现可以在 bean XML 配置中指定“init-method”和“destroy-method”的概念。

指定“init-method”的目的似乎是在创建 bean 时进行您可能想做的任何设置。这就是我感到困惑的地方。为什么您需要一个单独的方法来执行设置,而不是像普通/良好的面向对象设计那样仅使用构造函数来执行对象所需的任何设置?

换句话说,如果一个类需要一个依赖项,它不应该被注入到构造函数中吗,你知道已经被调用了,而对象可以在没有的状态下存在称之为“初始化方法”?

【问题讨论】:

  • OP 正试图与已回答的人进行辩论。这不是 SO 的预期用途。
  • 呃,是的。 Stackoverflow 旨在将最佳答案冒泡到顶部。确定哪些答案是最好的是社区的目的。否则评论系统将不存在。
  • 常见问题解答是这样说的:“如果您提出问题的动机是“我想参加关于 ______ 的讨论”,那么您不应该在这里提问。”。您的问题(对我而言)似乎是其中之一。无论如何,我不会参加:-)
  • 对;我不是在寻找关于这个问题的讨论。我正在评论您的答案,该答案没有解决问题,而是回答了“为什么需要初始化方法”。这不是被问到的,而是您的答案谈论的是与使用 init-method 和可用的构造函数注入不对应的事情。
  • "它为什么存在" != "你为什么要使用它"。我知道这是机械意图,问题提到了这一点。

标签: java spring dependency-injection


【解决方案1】:

如果您使用的是 setter 注入,那么肯定需要 init 方法/InitializingBean/@PostConstruct 以便在所有属性都已设置/自动装配后进行初始化。否则你有一个构造但不是依赖注入的对象。如果您使用的是构造函数注入,则很少需要它,在 Tomasz 和 Stephen 指出的情况下。

【讨论】:

    【解决方案2】:

    在少数情况下需要单独的init() 方法:

    • 您别无选择的旧版 API

    • 初始化有一些副作用,例如启动Thread,连接到外部资源

      这实际上具有更深层次的含义:当使用基于类的代理(通过)时,基类的构造函数被调用两次(第二次是从你的类继承的代理)- 仅调用init() 方法一次在最终对象上。

    • 你不应该在构造函数中执行虚拟调用(这实际上应该被编译器禁止......)

    • 有时您必须使用 setter/field 注入(尽管我喜欢构造函数注入),例如当使用上述基于类的代理时

    使用构造函数来执行对象所需的任何设置,就像正常/良好的面向对象设计一样?

    这实际上并不是将所有初始化代码放在构造函数中的最佳做法。构造函数中的副作用使测试和模拟变得更加困难。而是专注于创建 stableknown 状态的对象。这样你可以例如解耦创建一个管理连接池的对象并物理连接到该池。

    顺便说一句,destroy() 是一种没有析构函数的语言的福音,因为它允许您感激地关闭外部资源、中断线程等。经常使用它。

    【讨论】:

    • 为什么构造函数会有副作用?这对我来说没有意义。
    • 另外,为什么它会让测试变得更难?你不应该在构造函数中“新建”东西,或者利用具体的实现作为依赖。
    • 此外,使对象具有已知和稳定状态的方法不是在构造时执行所有必需的设置,从而不允许调用者通过设置它们来摆弄依赖关系?
    • 其实看看这篇文章。它解释了为什么构造函数注入似乎是强制依赖的方式:blog.springsource.org/2007/07/11/…
    • 支持构造函数注入的进一步证据:misko.hevery.com/2009/02/19/…
    【解决方案3】:

    你为什么需要它?

    在设置完所有 bean 的属性后调用 init 方法。如果 bean 需要对属性进行一些初始化或验证,而这些属性只能在设置完所有属性后完成,则通常需要这样做。 (如果您尝试在没有“init”回调的情况下执行此操作,您会发现每个属性设置器都必须检查其他设置器是否已被调用,依此类推。如果只能在所有属性之后进行初始化,那么即使该策略也会失败在一个循环的beans中已经设置好了。)

    如果bean持有需要显式释放的资源,则需要destroy方法;例如文件句柄、网络套接字、数据库连接。

    ... 像正常/良好的面向对象设计一样吗?

    任何规定 init 和 destroy 事件/方法是“错误”或“禁止”的设计方法都是不现实的,应该被忽略。事实上,面向对象的设计方法通常不会规定这一点。最多他们会说这种东西通常不需要。

    此外,DI 实际上在一定程度上改变了设计方法的规则……至少在初始化方面。特别是,通过将实例的“连接”外部化,它以经典 OO 设计方法没有预料到的方式从代码中提取出很多逻辑。如果有的话,这就是说需要根据依赖注入重新审视经典的 OO 方法。

    【讨论】:

    • 但这不一定能解决问题。这怎么可能不是通过构造函数注入和设置来完成的呢?当对象的构造函数的工作似乎正是这样做的时候,为什么我们会有一个“初始化方法”?构造对象?
    • 这个问题/答案似乎能更好地表达我的意思:stackoverflow.com/questions/4414471/…
    • @BenLakey - 它如何解决这个问题?
    • 它没有说明为什么不让构造函数执行对象构造的所有方面(传递给它的依赖项除外,它将分配给成员变量)。跨度>
    • 在我看来,在对象构造之后能够更改依赖项是一个坏主意,并且通过构造函数注入传入一组非具体的依赖项是一种更好的方法。如果您能说明为什么有更好的做法,无论如何,因为我看不到在构造时间之后使用单独的“初始化方法”的好处。
    猜你喜欢
    • 2014-06-04
    • 2021-02-24
    • 2011-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多