@Single 单例的
在Spring容器创建一个组件的时候,默认情况下只会创建一次,不同的组件公用一个对象,即单例的模式。
代码一
public interface Dao {
void query();
}
代码二
@Component
public class daoImpl implements Dao {
public void query() {
System.out.println("Impl");
}
}
代码三
@Service
public class ServiceImpl {
@Autowired
private Dao dao;
public void prient(){
System.out.println(dao.hashCode());
}
}
@Service
public class ServiceTwo {
@Autowired
private Dao dao;
public void prient(){
System.out.println(dao.hashCode());
}
}
代码四
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext acc=
new AnnotationConfigApplicationContext(Spring.class);
ServiceImpl service= (ServiceImpl) acc.getBean("serviceImpl");
service.prient();
ServiceTwo service2= (ServiceTwo) acc.getBean("serviceTwo");
service2.prient();
}
}
输出相同的hasCode .
将实现类daoImpl 重新修改一下做成作用域是原型的方式(prototype)
代码五
@Component
@Scope("prototype")
public class daoImpl implements Dao {
public void query() {
System.out.println("Impl");
}
}
将会输出不同的HasCode
通过官方的两个图片来详细描述一下Single和Prototype,首先看一下Single的。
解释一下图片,容器在初始化的时候只给第一个初始化的Bean开辟一个内存区域来存放这个实例,其它的Bean要想再创建一个Bean时不会开辟新的内存区域,会使用原来的。
Prototype 原型的方式:
容器在每一次创建一个Bean的时候都会去开辟一个新的内存区域,来存放对象,也就是同一个类的不同bean在Spring容器中也是不同的。
依赖和被依赖不同的作用域会造成问题
官网描述:
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依赖单例的,原型的依赖原型的。如果一个单例的依赖了一个非单例的,由于Spring在初始化的过程中只给单例的类一次创建机会,那么依赖的非单例也就失效了。
看一下相关代码:
代码一
@Component
@Scope("prototype")
public class daoImpl implements Dao {
public void query() {
System.out.println("Impl");
}
}
非单例的。
代码二
@Service
public class ServiceImpl {
@Autowired
private Dao dao;
public void prient(){
System.out.println(dao.hashCode());
}
}
需要依赖的类时单例的。
测试代码
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext acc=
new AnnotationConfigApplicationContext(Spring.class);
ServiceImpl service= (ServiceImpl) acc.getBean("serviceImpl");
service.prient();
ServiceImpl service2= (ServiceImpl) acc.getBean("serviceImpl");
service2.prient();
}
}
运行结果
726408598
726408598
Process finished with exit code 0
若何解决呢?
官网给出的解决方案是重写ApplicationContextAware的setApplicationContext方法
@Service
public class ServiceImpl implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void prient(){
Dao dao=createCommand();
System.out.println(dao.hashCode());
}
protected Dao createCommand() {
return this.applicationContext.getBean("dao", Dao.class);
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
运行结果
1594791957
1988644427
Process finished with exit code 0
由于上面的侵入性太高了,可以使用 @Lookup注解。
@Service
public abstract class ServiceImpl {
public void prient(){
Dao dao=createDao();
System.out.println(dao.hashCode());
}
@Lookup
protected abstract Dao createDao();
}
如果不想使用抽象方法可以返回一个null。
@Service
public class ServiceImpl {
public void prient(){
Dao dao=createDao();
System.out.println(dao.hashCode());
}
@Lookup
protected Dao createDao(){
return null;
}
}
这样也可以解决这种问题。
1677207406
1979274004
Process finished with exit code 0