spring 的bean默认是单例,这个用spring的人基本都知道。
如果需要多个实例,又要使用ioc怎么办呢?
当然是使用@Scope注解,指明ConfigurableBeanFactory.SCOPE_PROTOTYPE了。
/**
* Scope identifier for the standard singleton scope: "singleton".
* Custom scopes can be added via {@code registerScope}.
* @see #registerScope
*/
String SCOPE_SINGLETON = "singleton";
/**
* Scope identifier for the standard prototype scope: "prototype".
* Custom scopes can be added via {@code registerScope}.
* @see #registerScope
*/
String SCOPE_PROTOTYPE = "prototype";
但是在使用过程中发现这样依然是单例。
如果给一个组件加上
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
每次请求它的实例,spring的确会给返回一个新的。问题是这个多例对象是被其他单例服务依赖的。
而单例服务初始化的时候,多例对象就已经被创建好了。当你去使用单例服务的时候,多例对象也不会被再次创建了。
那怎么解决呢?
- 一个首先想到的方法当然是注入多个多例对象,就是写多个@Autowired。
但是我们的服务绝大多数时候都不知道需要多少多例服务,服务是动态创建的。
所以另一种方法就是使用getBean工厂方法:
@Autowired
private ApplicationContext applicationContext;
applicationContext.getBean() ;
- 其实spring的@Scope注解提供了在单例服务里使用多例对象的能力,它提供了一个代理字段
/**
* Specifies whether a component should be configured as a scoped proxy
* and if so, whether the proxy should be interface-based or subclass-based.
* <p>Defaults to {@link ScopedProxyMode#DEFAULT}, which typically indicates
* that no scoped proxy should be created unless a different default
* has been configured at the component-scan instruction level.
* <p>Analogous to {@code <aop:scoped-proxy/>} support in Spring XML.
* @see ScopedProxyMode
*/
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
默认是不使用代理创建。我们只要把它改成使用代理即可:
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
- 使用Spring的ObjectFactory
如果本来的代码是这样的
@Autowired
private MyPrototypeBean bean;
可以改成这样
@Autowired
private ObjectFactory<MyPrototypeBean> bean;
使用的时候调用getObject方法
bean.getObject()
- 使用java的JSR 330提出的Provider<T>
要引入jar包javax.inject
import javax.inject.Provider;
使用上和前面的ObjectFactory类似,只是方法名称变成了get
在我看来Provider和ObjectFactory的区别就是Resource和Autowire的区别。
- 使用Spring的Lookup注解
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.stereotype.Component;
@Component
public class MySingletonBean {
public void showMessage(){
MyPrototypeBean bean = getPrototypeBean();
//do your 自己的逻辑
}
@Lookup
public MyPrototypeBean getPrototypeBean(){
//spring自己会覆盖该方法
return null;
}
}
这样不用注入一个属性,但是要增加一个返回null的方法。
总结:
我倾向于使用第3和第5种方法。