Spring自动装配引发的一个问题
遇到的问题
我们知道Spring在注册bean的时候,默认单例的,但是在实际开发中(很少),我们使用的这个bean可能会给先给它设置初始的值,然后再去使用它,这个时候就会引发一个问题,如下代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class IndexService {
@Autowired
private IndexDao indexDao;
void test() {
indexDao.setName("aaaa");//这里我们设置了一个名字
System.out.println("service");
indexDao.test();
}
void test1() {
//但是这里我们不需要设置,如果设置了将会导致程序出错。
System.out.println("service");
indexDao.test();
}
//也就是说,我们每次使用indexDao的时候我们需要的都是新的,需要重新new一个,而不是spring给我创建的单例bean。
//这里使用的IndexService生命的是单例,所以indexDao只能赋值一次,这样产生的问题就是每次拿到的indexDaO都是同一个,和我们的预期不同。
}
我们看看官方是怎么说的
官方文档 ,下面摘自官方文档
1.4.6. Method Injection
In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the dependency by defining one bean as a property of the other. A problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container creates the singleton bean A only once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed.
大致意思是
在大多数应用程序场景中,大多数bean容器是单例的。当一个单例的bean需要与另一个单例bean或单体bean需要与另一个单体bean,您通常处理依赖通过定义一个bean的属性。问题是当bean的生命周期不同。假设单例bean需要使用单体(原型)的bean B,每个方法调用的容器创建单例bean只有一次,因此只得到一个机会来设置属性。容器不能每次都给bean提供一个新实例bean B。
解决的方法
官方提供了两种解决方案
官方文档往下拉,就能找到两种方式。
第一种
将上面讲到的bean B声明为原型 @Scope("prototype")
然后再bean中声明一个返回
下面是我的测试代码
package dao;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
@Repository
@Scope("prototype")//声明此类为原型
public class IndexDaoImp implements IndexDao {
private String name;
public void setName(String name) {
this.name = name;
}
public void test() {
System.out.println("dao1");
}
}
package dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class IndexService {//第二种写法,需要添加abstract变成抽象类
@Lookup("indexDaoImp")//思考这里为什么要写indexDaoImp?
protected IndexDao createIndexDao() {
return null;
}
//第二种写法,官方写法
// @Lookup("indexDaoImp")
// protected abstract IndexDao createIndexDao() ;
@Autowired
private IndexDao indexDao;//思考:这里为什么写成这样也能将indexDao注入,而不写成indexDaoImp?
void test() {
System.out.println("service test:"+createIndexDao());//每次打印出来都不一样
System.out.println("service test:"+indexDao);//每次打印出来都一样
indexDao.test();
}
void test1() {
System.out.println("service test1:"+createIndexDao());//每次打印出来都不一样
System.out.println("service test1:"+indexDao);//每次打印出来都一样
indexDao.test();
}
}
第二种
因为不适合,所以略…
,
github:https://github.com/Wnghao/Spring-ioc
至此,问题结束!