Spring中的多例往单例里注入的方法

3,333 阅读2分钟
原文链接: www.jianshu.com

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的确会给返回一个新的。问题是这个多例对象是被其他单例服务依赖的。

而单例服务初始化的时候,多例对象就已经被创建好了。当你去使用单例服务的时候,多例对象也不会被再次创建了。

那怎么解决呢?

  1. 一个首先想到的方法当然是注入多个多例对象,就是写多个@Autowired。

但是我们的服务绝大多数时候都不知道需要多少多例服务,服务是动态创建的。

所以另一种方法就是使用getBean工厂方法:

@Autowired  
private ApplicationContext applicationContext;  
applicationContext.getBean() ;
  1. 其实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)  
  1. 使用Spring的ObjectFactory
    如果本来的代码是这样的
@Autowired  
private MyPrototypeBean bean; 

可以改成这样

@Autowired  
private ObjectFactory<MyPrototypeBean> bean; 

使用的时候调用getObject方法

bean.getObject()  
  1. 使用java的JSR 330提出的Provider<T>
    要引入jar包javax.inject
import javax.inject.Provider;  

使用上和前面的ObjectFactory类似,只是方法名称变成了get

在我看来Provider和ObjectFactory的区别就是Resource和Autowire的区别。

  1. 使用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种方法。