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

Pet*_*sik 16 java dependency-injection cdi

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

@Inject
MyBean bean;
Run Code Online (Sandbox Code Playgroud)

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

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

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

Ren*_*ink 20

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

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

OtherBean b = ....;
new MyBean(b);
Run Code Online (Sandbox Code Playgroud)

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

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

塞特与构造函数注入

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

对于setter注入,这意味着您可能需要更复杂的状态处理,因为对象在构造之后应该处于有效状态,即使尚未调用setter也是如此.因此,即使未设置属性,对象也必须处于有效状态.例如,使用默认值或null对象.

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

所以不要写这样的课

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;
  }

}
Run Code Online (Sandbox Code Playgroud)

你应该使用构造函数注入

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

我的结论

  • 为每个可选依赖项使用属性.
  • 对每个强制依赖使用构造函数args.

PS:我的博客pojos和java bean之间的区别更详细地解释了我的结论.

  • 构造函数注入还允许将类字段声明为`final`.使用属性或setter注入时,这是不可能的. (3认同)
  • `这会产生大量不安全/容易被破坏的代码`是这样做还是只是使依赖噩梦可见?我的意思是,如果对象具有强制依赖项,您可以使用 setter 隐藏它们,也可以编写一些 javadoc,例如“必须先调用此 setter”,或者您可以使它们显式。我还没有看到你的代码,但如果你说你有一个“更复杂的类层次结构和许多抽象超类”,我猜这是一个设计问题。我通常更喜欢组合而不是子类化。 (3认同)
  • 同样,通过构造函数注入,您可以使bean不可变。 (2认同)