我们知道,拥有prototype类型scope的bean,在请求方每次向容器请求该类型对象的时候,容器都会返回一个全新的该对象实例。
我们看下面的例子:
public class MockNewsPersister implements IFXNewsPersister { 
  private FXNewsBean newsBean; 
  
  public void persistNews(FXNewsBean bean) { 
   persistNewes();  
   } 
  public void persistNews() { 
   System.out.println("persist bean:"+getNewsBean()); 
  } 
  public FXNewsBean getNewsBean() { 
   return newsBean;   
   } 
 
  public void setNewsBean(FXNewsBean newsBean) { 
   this.newsBean = newsBean; 
  } 
} 
相应的xml为:
<bean id="newsBean" class="..domain.FXNewsBean" singleton="false"> 
</bean> 
<bean id="mockPersister" class="..impl.MockNewsPersister"> 
  <property name="newsBean"> 
   <ref bean="newsBean"/> 
  </property> 
</bean>  
我们看测试代码
BeanFactory container = new XmlBeanFactory(new ClassPathResource("..")); 
MockNewsPersister persister = (MockNewsPersister)container.getBean("mockPersister"); 
persister.persistNews(); 
persister.persistNews(); 
输出: 
persist bean:..domain.FXNewsBean@1662dc8 
persist bean:..domain.FXNewsBean@1662dc8 
为什么两次persister.persistNews(); 打印的内容一样?
那么凭什么为什么两次persister.persistNews(); 打印的内容要一样?
不是说请求singleton="false"的对象时每次返回的都是不一样的么?
请求singleton="false"的对象时每次返回的对象是不一样的,这句话没错,但是在上面的情况中,MockNewsPersister请求了几次newsBean呢?
请求了一次。
换句话说,FXNewsBean的构造方法只执行了一次。

那我怎么在一个singleton的对象里,动态地获得prototype型的bean呢(就是每次获得的bean都不一样)?********

至少有三个答案


使用方法注入

xml如下
<bean id="newsBean" class="..domain.FXNewsBean" singleton="false"> 
</bean> 
<bean id="mockPersister" class="..impl.MockNewsPersister"> 
  <lookup-method name="getNewsBean" bean="newsBean"/> 
</bean>  
lookup-method的意思就是:spring通过cglib技术,为MockNewsPersister生成一个子类,并复写其getNewBean方法。
之后每次调用mockPersister的getNewsBean方法都动态的返回一个newsBean实例。
当然MockNewsPersister的getNewsBean必须满足下面的形式
<public|protected> [abstract] <return-type> theMethodName(no-arguments); 
换句话说,就是getNewsBean必须能被复写。

其实我们大可以直接在persister.persistNews()里new一个FXNewsBean,而不用非得从容器里获得。这里引这个例子,只是为了说明方法注入。


使用BeanFactoryAware接口

其实即使没有方法注入,在上面的例子中,在getNewsBean中,只要我们调用BeanFactory.getBean("newsBean")也能动态的获得newsbean。
问题是,如果在一个普通的bean中获得BeanFactory的引用?
Spring框架提供了一个BeanFactoryAware接口,容器在实例化实现了该接口的bean定义的过程中,会自动将容器本身注入该bean。这样,该bean就持有了它所处的BeanFactory的引用。
BeanFactoryAware的定义如下代码所示: 
public interface BeanFactoryAware { 
  void setBeanFactory(BeanFactory beanFactory) throws BeansException; 
  } 
实现BeanFactoryAware接口的情况:
public class MockNewsPersister implements IFXNewsPersister,BeanFactoryAware { 
  private BeanFactory beanFactory; //  待注入的beanFactory
  
  public void setBeanFactory(BeanFactory bf) throws BeansException { 
   this.beanFactory = bf; 
  } 
  public void persistNews(FXNewsBean bean) {    
	persistNews(); 
  } 
  public void persistNews() 1  { 
   System.out.println("persist bean:"+getNewsBean()); 
  } 
  public FXNewsBean getNewsBean() { 
  return beanFactory.getBean("newsBean");   //使用beanFactory
  } 
} 
xml简化为 
<bean id="newsBean" class="..domain.FXNewsBean" singleton="false"> 
</bean> 
<bean id="mockPersister" class="..impl.MockNewsPersister"> 
</bean>  
如此,可以预见到,输出的结果将与我们所预期的相同:   
persist bean:..domain.FXNewsBean@121cc40 
persist bean:..domain.FXNewsBean@1e893df  
实际上,方法注入动态生成的子类,完成的是与以上类似的逻辑,只不过实现细节上不同而已。 

使用ObjectFactoryCreatingFactoryBean

我们先看实现,再讲原理
将MockNewsPersister改成如下的样子
public class MockNewsPersister implements IFXNewsPersister { 
  private ObjectFactory newsBeanFactory;  //就是ObjectFactoryCreatingFactoryBean
  
  public void persistNews(FXNewsBean bean) { 
   persistNews(); 
  } 
  public void persistNews() 
  { 
   System.out.println("persist bean:"+getNewsBean()); 
  } 
  public FXNewsBean getNewsBean() { 
   return newsBeanFactory.getObject(); 
  } 
  public void setNewsBeanFactory(ObjectFactory newsBeanFactory) { 
   this.newsBeanFactory = newsBeanFactory; 
  } 
}  


xml配置
<bean id="newsBean" class="..domain.FXNewsBean" singleton="false"> 
</bean> 
<bean id="newsBeanFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"> 
  <property name="targetBeanName"> 
   <idref bean="newsBean"/> 
  </property> 
</bean> 
<bean id="mockPersister" class="..impl.MockNewsPersister"> 
   <property name="newsBeanFactory"> 
   <ref bean="newsBeanFactory"/> 
  </property> 
</bean> 


上面的<idref bean="newsBean"/> 
ref是获取这个bean的实例。用来实现注入功能。
假如只是想获取bean的名称 采用idref
使用idref标记允许容器在部署时,验证所被引用的bean是否存在。
等效于:
<bean id="newsBeanFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
    <property name="targetBeanName" value="newsBeanFactory" />
</bean> 
ObjectFactoryCreatingFactoryBean是 Spring 提供的一个 FactoryBean实现,它返回一个ObjectFactory实例。从ObjectFactoryCreatingFactoryBean返回的这个ObjectFactory实例可以为我们返回容器管理的相关对象。实际上,ObjectFactoryCreatingFactoryBean实现了BeanFactoryAware接口,它返回的ObjectFactory实例只是特定于与Spring容器进行交互的一个实现而已。使用它的好处就是, 隔离了客户端对象对BeanFactory的直接引用
由于ObjectFactoryCreatingFactoryBean实现了BeanFactoryAware接口,所以ObjectFactoryCreatingFactoryBean里面也持有当前beanFactory的引用。
newsBeanFactory.getObject(),其实就是ObjectFactoryCreatingFactoryBean的getObject。
而ObjectFactoryCreatingFactoryBean内并没有getObject,要去它的父类AbstractFactoryBean找:
         //AbstractFactoryBean.java
	//singleton是个boolean型 默认是ture
	/**
	 * Expose the singleton instance or create a new prototype instance.
	 * @see 。createInstance()
	 * @see 。getEarlySingletonInterfaces()
	 */
	@Override
	public final T getObject() throws Exception {
		if (isSingleton()) {      
			return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());
		}
		else {
			return createInstance();
		}
	}


	//afterPropertiesSet会在容器启动时被调用 
	/**
	 * Eagerly create the singleton instance, if necessary.
	 */
	public void afterPropertiesSet() throws Exception {
		if (isSingleton()) {
			this.initialized = true;
			this.singletonInstance = createInstance();
			this.earlySingletonInstance = null;
		}
	}
	//看到了吧 模板方法 具体的实现 要去子类里看 子类是谁呢?
	//是ObjectFactoryCreatingFactoryBean
	/**
	 * Template method that subclasses must override to construct
	 * the object returned by this factory.
	 * <p>Invoked on initialization of this FactoryBean in case of
	 * a singleton; else, on each {@link 。getObject()} call.
	 * @return the object returned by this factory
	 * @throws Exception if an exception occured during object creation
	 * @see 。getObject()
	 */
	protected abstract T createInstance() throws Exception;

不知道大家是否看懂了里面的实现过程
1 容器启动时就调用了AbstractFactoryBean的afterPropertiesSet方法。
2 afterPropertiesSet里
                        initialized = true;
this.singletonInstance = createInstance();
   这个createInstance其实是在子类ObjectFactoryCreatingFactoryBean里实现的。
3 ObjectFactoryCreatingFactoryBean的createInstance方法返回了一个TargetBeanObjectFactory,这TargetBeanObjectFactory能从spring容器里按照targetBeanName获得bean。


感谢glt


参考资料

http://blog.csdn.net/caihaijiang/article/details/5903227
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐