我来自.NET背景,使用Unity,Ninject,Castle Windsor等依赖注入容器。最近我开始学习使用Spring的依赖注入功能。
在学习Spring时,我已经看到可以在bean XML配置上指定'init-method'和'destroy-method'的概念。
指定'init-method'的目的似乎是在bean创建时进行任何你可能想要做的设置。这是我感到困惑的地方。为什么你需要一个单独的方法来执行设置,而不是只需使用构造函数来执行对象所需的任何设置,就像正常/良好的面向对象设计所指示的那样?
换句话说,如果一个类需要一个依赖项,那么它不应该被注入构造函数中,而知道已被调用,而该对象可能存在于一个没有的状态称它为'init-method'?
答案 0 :(得分:9)
在极少数情况下需要单独的init()
方法:
您没有任何选择的旧版API
初始化具有一些副作用,例如开始Thread
,连接到som外部资源
这实际上具有更深层次的含义:当使用基于类的代理(通过cglib)时,基类的构造函数被调用两次(第二次是代理的,而不是来自您的类) - init()
方法在最终对象上只调用一次。
你不应该在构造函数中执行虚拟调用(这实际上应该被编译器禁止...)
有时你必须使用setter / field injection(虽然我喜欢构造函数注入),例如当使用上述基于类的代理时
使用构造函数来执行对象所需的任何设置,就像正常/良好的面向对象设计一样?
这实际上并不是将所有初始化代码放在构造函数中的最佳实践。构造函数中的副作用使测试和模拟变得更加困难。而是专注于在稳定和已知状态中创建对象。这样你就可以这样解耦创建管理连接池的对象并物理连接到该池。
BTW destroy()
是一种没有析构函数的语言的祝福,因为它允许你感激地关闭外部资源,中断线程等。经常使用它。
答案 1 :(得分:5)
你为什么需要它?
在设置了所有bean的属性之后调用init方法。如果bean需要对属性进行一些初始化或验证,而这些属性只能在设置了所有属性后才能完成,则通常需要这样做。 (如果你试图在没有“init”回调的情况下这样做,你会发现每个属性设置器都必须检查是否已经调用了其他setter。等等。如果只能在所有属性之后进行初始化,那么即使该策略也会失败已经设定了豆类循环。)
如果bean拥有需要显式释放的资源,则需要使用destroy方法;例如文件句柄,网络套接字,数据库连接。
......就像正常/良好的面向对象设计一样?
任何指示init和destroy事件/方法是“错误”或“禁止”的设计方法都是不现实的,应该被忽略。事实上,面向对象的设计方法通常不会指明这一点。他们至多会说通常不需要这种东西。
此外,DI实际上在某种程度上改变了设计方法的规则......至少在初始化方面。特别是,通过外部化实例的“连接”,它以经典的OO设计方法无法预期的方式从代码中抽出很多逻辑。如果有的话,这就是说需要根据依赖注入来重新审视经典的OO方法。
答案 2 :(得分:0)
如果使用setter注入,则必须使用init方法/ InitializingBean / @ PostConstruct,以便在设置/自动装配所有属性后进行初始化。否则,您有一个构造但不依赖注入的对象。如果你正在使用构造函数注入,那么在Tomasz和Stephen指出的情况下很少需要它。