构造函数和属性注入使您可以选择在非 CDI 环境中轻松初始化对象,例如单元测试。
在非 CDI 环境中,您仍然可以通过传递构造函数 arg 来简单地使用该对象。
OtherBean b = ....;
new MyBean(b);
如果只使用字段注入,通常必须使用反射来访问字段,因为字段通常是私有的。
如果您使用属性注入,您还可以在 setter 中编写代码。例如,验证代码或清除内部缓存,这些缓存包含从设置器修改的属性派生的值。您想要做什么取决于您的实施需求。
Setter 与构造函数注入
在面向对象的编程中,对象在构造后必须处于有效状态,并且每次方法调用都会将状态更改为另一个有效状态。
对于 setter 注入,这意味着您可能需要更复杂的状态处理,因为对象在构造后应该处于有效状态,即使尚未调用 setter。因此,即使未设置属性,对象也必须处于有效状态。例如,通过使用默认值或空对象。
如果对象的存在和属性之间存在依赖关系,则属性应该是构造函数参数。这也将使代码更干净,因为如果您使用构造函数参数,则说明依赖项是必要的。
所以不要像这样写一个类
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;
}
}
我的结论
- 为每个可选依赖项使用属性。
- 对每个强制依赖项使用构造函数参数。
PS:我的博客pojos和java beans的区别更详细的解释了我的结论。
编辑
Spring 还建议使用构造函数注入,正如我在 spring 文档的Setter-based Dependency Injection部分中找到的那样。
Spring 团队通常提倡构造函数注入,因为它允许您将应用程序组件实现为不可变对象,并确保所需的依赖项不为空。此外,构造函数注入的组件总是以完全初始化的状态返回给客户端(调用)代码。附带说明一下,大量的构造函数参数是一种不好的代码气味,这意味着该类可能有太多的职责,应该重构以更好地解决适当的关注点分离问题。
Setter 注入应该主要只用于可以在类中分配合理默认值的可选依赖项。否则,必须在代码使用依赖项的任何地方执行非空检查。setter 注入的一个好处是 setter 方法使该类的对象可以在以后重新配置或重新注入。因此,通过 JMX MBean 进行管理是 setter 注入的一个引人注目的用例。
当您考虑单元测试时,构造函数注入也是一种更好的方法,因为调用构造函数而不是设置私有(@Autowired)字段更容易。