日暮途远

日暮途远,涸辙难行;东隅已逝,桑榆非晚

Constructor vs. Setter injection

最近在查阅 spring4 的官方文档时,发现一个很有趣的地方,就是:究竟我们在使用 DI 时,应该采用基于构造器的注入,还是应该使用基于 Setter 的注入?对此,spring 官方竟然也玩起了墙头草,在 4.x 系列中推翻了之前的建议,且看之前在 3.x 中的文档描述:

Since you can mix both, Constructor- and Setter-based DI, it is a good rule of thumb to use constructor arguments for mandatory dependencies and setters for optional dependencies. Note that the use of a @Required annotation on a setter can be used to make setters required dependencies.

The Spring team generally advocates setter injection, because large numbers of constructor arguments can get unwieldy, especially when properties are optional. Setter methods also make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is a compelling use case.

Some purists favor constructor-based injection. Supplying all object dependencies means that the object is always returned to client (calling) code in a totally initialized state. The disadvantage is that the object becomes less amenable to reconfiguration and re-injection.

Use the DI that makes the most sense for a particular class. Sometimes, when dealing with third-party classes to which you do not have the source, the choice is made for you. A legacy class may not expose any setter methods, and so constructor injection is the only available DI.

以上的摘要来自 spring3.1.x 的文档,注意标红标粗的地方,明确说明他们是建议采用 Setter 的方式来注入依赖的,当然,他们也说明了这么建议的原因(见后面 constructor 方式的优缺点)。

但是,到了 spring4 系列的文档中,则画风一变,成了如下的描述:

Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies. Note that use of the @Required annotation on a setter method can be used to make the property a required dependency.

The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.

Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is therefore a compelling use case for setter injection.

Use the DI style that makes the most sense for a particular class. Sometimes, when dealing with third-party classes for which you do not have the source, the choice is made for you. For example, if a third-party class does not expose any setter methods, then constructor injection may be the only available form of DI.

同样,官方也列出来这样做的原因,总结下来如下,采用 constructor based DI 有如下的好处:

  1. 可以构造一个不可变的组件;
  2. 可以确保必须的组件不为空(当然,这需要你自己在编码构造器的时候进行非空判定,建议 assert 处理);
  3. 另外,这种方式在构造时,总是可以返回一个可以立即使用的组件,而不是需要额外的再配置,这一点在非 spring 环境下很有用;

当然,这样做也有他的缺点,就是如果所依赖的组件过多,则做导致构造方法非常的 bad smell ,同时,如果有多个构造方法时,将变得很怪;

 

不管是技术还是理论都是在不是断演变和进步的,可以预见,在后续相当长的一段时间内,spring4,5,6… 将仍然会是后续新的业界规范,因此,我们遵循官方最新的推荐,尽量采用基于 constructor 的方式来管理我们的依赖注入。

这里还有非常重要的一点,官方文档里面没有说明:那就是循环依赖的问题,我们应该尽量避免循环依赖,但如果出现循环依赖该怎么选择?

答案是没得选择,只有一个选项:setter based

例如,如果组件 A 依赖 B,但同时,B 也依赖 A,那么如果你采用 constructor based DI 的话,你将得到一个 BeanCurrentlyInCreationException ,但是 setter DI 是没问题的,因为 setter DI 的对象是 optinal 的,是 nullable 的。

另外,还有一点未说明的是 Autowired 注解还适用于 Field,即字段注入模式,未说明的原因是 Field 注入的本质还是 Setter ,这一点我们在其源码 AutowiredAnnotationBeanPostProcessor 中可以看到:

 

点赞
  1. 匿名说道:

    我靠,看英文文档都这么细致啊,这种细节都能发现!大牛果然不是盖的! :evil:

  2. I have never carried out much free movement quilting earlier than and didn't have time
    to excellent my skills and get it executed in time so I made a decision to make one last ditch effort to find a quilter that could do
    a brilliant fast turnaround.

发表评论

电子邮件地址不会被公开。

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">