为什么在 CDI 中使用构造函数而不是 setter 注入?

2024-05-20

我在这里找不到任何合理的答案,所以我希望它不是重复的。那么为什么我应该更喜欢 setter 或构造函数注入而不是简单的注入

@Inject
MyBean bean;

如果您需要在类初始化期间对注入的 bean 执行某些操作,我会使用构造函数注入,例如

public void MyBean(@Inject OtherBean bean) {
    doSomeInit(bean);
    //I don't need to use @PostConstruct now
}

但仍然几乎一样@PostConstruct方法并且我根本没有得到setter注入,这难道不是Spring和其他DI框架之后的遗物吗?


构造函数和属性注入使您可以选择轻松地在非 CDI 环境中初始化对象,例如单元测试。

在非 CDI 环境中,您仍然可以通过传递构造函数参数来简单地使用该对象。

OtherBean b = ....;
new MyBean(b);

如果您只使用字段注入,则通常必须使用反射来访问字段,因为字段通常是私有的。

如果您使用属性注入,您还可以在 setter 中编写代码。例如。验证代码,或者清除保存从 setter 修改的属性派生的值的内部缓存。您想要做什么取决于您的实施需求。

Setter 与构造函数注入

在面向对象编程中,对象在构造后必须处于有效状态,并且每次方法调用都会将状态更改为另一个有效状态。

对于 setter 注入,这意味着您可能需要更复杂的状态处理,因为对象在构造后应该处于有效状态,即使尚未调用 setter。因此,即使未设置该属性,该对象也必须处于有效状态。例如。通过使用默认值或空对象 https://en.wikipedia.org/wiki/Null_Object_pattern.

如果对象的存在和属性之间存在依赖关系,则属性应该是构造函数参数。这也将使代码更加干净,因为如果您使用构造函数参数,您就会记录依赖项是必要的。

所以不要写这样的类

public class CustomerDaoImpl implements CustomerDao {
 
  private DataSource dataSource;
 
  public Customer findById(String id){
     checkDataSource();

     Connection con = dataSource.getConnection();
     ...
     return customer;
  }

  private void checkDataSource(){
     if(this.dataSource == null){
         throw new IllegalStateException("dataSource is not set");
     }
  }

 
  public void setDataSource(DataSource dataSource){
     this.dataSource = dataSource;
  }
 
}

你应该使用构造函数注入

public class CustomerDaoImpl implements CustomerDao {
 
  private DataSource dataSource;
 
  public CustomerDaoImpl(DataSource dataSource){
      if(dataSource == null){
        throw new IllegalArgumentException("Parameter dataSource must not be null");
     }
     this.dataSource = dataSource;
  }
 
  public Customer findById(String id) {    
      Customer customer = null;
     // We can be sure that the dataSource is not null
     Connection con = dataSource.getConnection();
     ...
     return customer;
  }
}

我的结论

  • Use 特性对于每一个可选依赖项.
  • Use 构造函数参数对于每一个强制依赖.

PS:我的博客pojos和java beans的区别 https://www.link-intersystems.com/blog/2011/09/26/the-difference-between-pojos-and-java-beans/更详细地解释了我的结论。

EDIT

Spring 还建议使用构造函数注入,正如我在 spring 文档部分中找到的那样基于 Setter 的依赖注入 https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-setter-injection.

Spring 团队通常提倡构造函数注入,因为它允许您将应用程序组件实现为不可变对象,并确保所需的依赖项不为空。此外,构造函数注入的组件始终以完全初始化的状态返回给客户端(调用)代码。附带说明一下,大量的构造函数参数是一种不好的代码味道,这意味着该类可能有太多的职责,应该进行重构以更好地解决适当的关注点分离问题。

Setter 注入主要应该仅用于可以在类中分配合理默认值的可选依赖项。否则,必须在代码使用依赖项的所有地方执行非空检查。 setter 注入的好处之一是 setter 方法使该类的对象可以在以后重新配置或重新注入。因此,通过 JMX MBean 进行管理是 setter 注入的一个引人注目的用例。

当您考虑单元测试时,构造函数注入也是一种更好的方法,因为调用构造函数比设置私有(@Autowired)字段更容易。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么在 CDI 中使用构造函数而不是 setter 注入? 的相关文章

随机推荐