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

我在这里找不到任何合理的答案所以我希望它不是重复的。 那么为什么我更喜欢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环境中初始化对象,例如unit testing。

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

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

如果您只是使用场注入,则必须使用reflection来访问该字段(例如,如果它是私有的)。

如果使用属性注入,也可以在setter中编写代码。 所以这取决于您的实施需求。

塞特与构造函数注入

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

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

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

所以不要写这样的课

 public class CustomerDaoImpl implements CustomerDao { private DataSource dataSource; public Customer findById(String id){ // Is the dataSource set?! Connection con = dataSource.getConnection(); ... return customer; } 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; } } 

我的结论

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

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

使用CDI时 ,没有任何理由使用构造函数或setter注入。 如问题中所述,您可以为构造函数中的其他方法添加@PostConstruct方法。

其他人可能会说你需要使用Reflection在unit testing中注入字段,但事实并非如此; 模拟库和其他测试工具为您做到这一点。

最后,构造函数注入允许字段为final ,但这并不是@Inject -annotated字段(不能是final )的缺点。 注释的存在,加上没有明确设置字段的任何代码,应该清楚地表明它只能由容器(或测试工具)设置。 在实践中,没有人会重新分配注入的场。

构造函数和setter注入在过去是有意义的,当开发人员通常不得不手动实例化并将依赖项注入到测试对象中时。 如今,技术已经发展,现场注入是一个更好的选择。