我在SO上找不到任何合理的答案所以我希望它不是重复的。那么为什么我更喜欢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框架之后的遗留物吗?
答案 0 :(得分:16)
构造函数和属性注入使您可以轻松地在非CDI环境中初始化对象,例如单元测试。
在非CDI环境中,您仍然可以通过传递构造函数arg来简单地使用该对象。
OtherBean b = ....;
new MyBean(b);
如果你只是使用场注入,你通常必须使用反射来访问该字段,因为字段通常是私有的。
如果使用属性注入,您还可以在setter中编写代码。例如。验证代码或清除内部缓存,其中包含从setter修改的属性派生的值。您想要做什么取决于您的实施需求。
在面向对象的编程中,对象必须在构造之后处于有效状态,并且每个方法调用都会将状态更改为另一个有效状态。
对于setter注入,这意味着您可能需要更复杂的状态处理,因为在构造之后对象应该处于有效状态,即使尚未调用setter也是如此。因此,即使未设置属性,对象也必须处于有效状态。例如。使用默认值或null object。
如果对象的存在与属性之间存在依赖关系,则该属性应该是构造函数参数。这也将使代码更加干净,因为如果使用构造函数参数,则需要记录依赖项。
所以不要像这样写一个类
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:我的博客The difference between pojos and java beans更详细地解释了我的结论。
答案 1 :(得分:4)
使用CDI 时,没有理由使用构造函数或setter注入。如问题所述,您可以为构造函数中的其他方法添加@PostConstruct
方法。
其他人可能会说你需要使用Reflection在单元测试中注入字段,但事实并非如此;模拟库和其他测试工具为您做到这一点。
最后,构造函数注入允许字段为final
,但这并不是@Inject
- 注释字段(不能是final
)的缺点。注释的存在,加上没有明确设置字段的任何代码,应该清楚地表明它只能由容器(或测试工具)设置。在实践中,没有人会重新分配注入的场地。
过去,构造函数和setter注入是有意义的,因为开发人员通常必须手动实例化并将依赖项注入到测试对象中。如今,技术已经发展,现场注入是一个更好的选择。
答案 2 :(得分:1)
可接受的答案很好,但是并不能归功于构造函数注入的主要优点-类不变性,这有助于实现线程安全性,状态安全性以及对类的更好可读性
考虑到您的类具有依赖项,并且所有这些依赖项都作为构造函数参数提供,那么您可以知道该对象永远不会在依赖项无效的状态下存在。不需要这些依赖项的setter(只要它们是私有的),因此对象被实例化为完全状态或完全不被实例化。
不可变对象在多线程应用程序中的运行可能性更大。尽管仍然需要使该类在内部具有线程安全性,但是您不必担心外部客户端会协调对对象的访问。
当然,这仅在某些情况下有用。 Setter注入非常适合局部依赖,例如,我们在一个类中具有3个属性,并具有3个arg构造函数和setters方法。在这种情况下,如果只想传递一个属性的信息,则只能通过setter方法传递。对于测试目的非常有用。