刚开始接触Spring的时候一些基础的教学在说到Bean的创建时会提到Spring的单例模式,就是说默认情况下Spring中定义的Bean是以单例模式创建的。如果以前了解设计模式中的单例模式的话很容易对这种说法产生先入为主的印象。事实上,Spring中的单例模式还有许多需要注意的地方。
在GoF中的单例模式是指一个ClassLoader中只存在类一个实例。
而在Spring中的单例实际上更确切的说应该是:
1.每个Spring Container中定义的Bean只存在一个实例
2.每个Bean定义只存在一个实例。
如果对Spring的单例模式不够了解在设计与开发过程中会留下很多隐患。以下是根据Spring中单例模式的特点写的测试代码,通过代码可以更直观的了解Spring中的单例模式。
先看一下这个测试中用到的类已经XML中Bean的定义.随后给出Main方法中的测试代码:
根据以上类定义,执行以下main方法进行测试:
package beanscope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanScopceMain
{
/**
* @comment [注释说明]
* @author 荣磊, 2012-7-17
*
* @param args
*/
public static void main(String[] args)
{
String springConfig = "beanscope/spring-config.xml";
//创建一个SpringContainer
ApplicationContext context = new ClassPathXmlApplicationContext(springConfig);
//通过SpringContainer取得runnerBeanOne
RunnerBeanRefToSingletonBean runnerBeanOne =
context.getBean("runnerBeanOne", RunnerBeanRefToSingletonBean.class);
//通过SpringContainer取得runnerBeanTwo
RunnerBeanRefToSingletonBeanTwo runnerBeanTwo =
context.getBean("runnerBeanTwo", RunnerBeanRefToSingletonBeanTwo.class);
//通过SpringContainer取得singletonBean
SingletonBean singletonBean =
context.getBean("singletonBean", SingletonBean.class);
//输出上一次操作singltonBean的Bean名称,
//因为在这之前没有对singletonBean的lastOperatedBy属性进行初始化,所以这里应该会输出none
singletonBean.showLastOperateBean();
//设置runnerBeanOne中singletonBean对象的上一次操作者信息为runnerBeanOne
runnerBeanOne.setMyNameToSingletonBean();
//通过singletonBean输出上一次操作者信息
singletonBean.showLastOperateBean();
//输出runnerBeanTwo中应用的singletonBean对象的上一次操作者信息
runnerBeanTwo.showLastOprBeanOfSingletonBean();
//设置runnerBeanOne中singletonBean对象的上一次操作者信息为runnerBeanTwo
runnerBeanTwo.setMyNameToSingletonBean();
//通过singletonBean输出上一次操作者信息
singletonBean.showLastOperateBean();
//输出runnerBeanOne中应用的singletonBean对象的上一次操作者信息
runnerBeanOne.showLastOprBeanOfSingletonBean();
}
}
输出结果:
从这个执行结果来看已经可以看出单例模式的影子,两个分别创建的runnerBean对象在定义中都依赖于singletonBean对象,第2步中,使用runnerBeanOne.setMyNameToSingletonBean()方法,设置了runnerBeanOne自己引用的singletonBean的操作者信息,随后输出了runnerBeanTwo自己引用的singletonBean和单独创建的singletonBean的操作者信息,结果都是runnerBeanOne,这就说明,当runnerBeanOne操作自己引用的singletonBean时,对runnerBeanTwo中和单独创建的singletonBean产生了影响,也就是他们所引用的singletonBean对象是同一个对象。
这是因为runnerBeanOne与runnerBeanTwo在Spring定义Bean时都设置了一个property ref引用到了同一个singletonBean的Bean定义。 这就是最前面说道的两点中的第二点:2.每个Bean定义只存在一个实例。 如果在Spring配置文件中定义runnerBeanOne与runnerBeanTwo时引用到不同的singletonBean定义,就会创建出两个不同的singletonBean对象,即使两个Bean定义实际上对应的class是相同的。如下:
更改配置文件后执行main方法类,输出:
none
runnerBeanOne
none
runnerBeanOne
runnerBeanOne
可以看出runnerBeanOne与runnerBeanTwo中引用的singletonBean已经不是同一个对象了,因为虽然都是SingletonBean类,但是在配置文件中作出了两个Bean的定义,而Spring的单例模式只保证每个Bean的定义只存在一个实例。
接下来看前面说到的第一点,1.每个Spring Container中定义的Bean只存在一个实例。
使用改动之前的Spring配置文件,并更改main方法类如下:
package beanscope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanScopceMain
{
/**
* @comment [注释说明]
* @author 荣磊, 2012-7-17
*
* @param args
*/
public static void main(String[] args)
{
String springConfig = "beanscope/spring-config.xml";
//创建一个SpringContainer
ApplicationContext context = new ClassPathXmlApplicationContext(springConfig);
/****************************
* 创建另外一个SpringContainer
********/
ApplicationContext context2 = new ClassPathXmlApplicationContext(springConfig);
//通过SpringContainer取得runnerBeanOne
RunnerBeanRefToSingletonBean runnerBeanOne =
context.getBean("runnerBeanOne", RunnerBeanRefToSingletonBean.class);
/**************************************************
* 通过新创建的另外一个SpringContainer取得runnerBeanTwo
*****************/
RunnerBeanRefToSingletonBeanTwo runnerBeanTwo =
context2.getBean("runnerBeanTwo", RunnerBeanRefToSingletonBeanTwo.class);
//通过SpringContainer取得singletonBean
SingletonBean singletonBean =
context.getBean("singletonBean", SingletonBean.class);
//输出上一次操作singltonBean的Bean名称,
//因为在这之前没有对singletonBean的lastOperatedBy
//属性进行初始化,所以这里应该会输出none
singletonBean.showLastOperateBean();
//设置runnerBeanOne中singletonBean对象的上一次操作者
//信息为runnerBeanOne
runnerBeanOne.setMyNameToSingletonBean();
//通过singletonBean输出上一次操作者信息
singletonBean.showLastOperateBean();
//输出runnerBeanTwo中应用的singletonBean对象的上一次操作者信息
runnerBeanTwo.showLastOprBeanOfSingletonBean();
//设置runnerBeanOne中singletonBean对象的上一次操作者信息为runnerBeanTwo
runnerBeanTwo.setMyNameToSingletonBean();
//通过singletonBean输出上一次操作者信息
singletonBean.showLastOperateBean();
//输出runnerBeanOne中应用的singletonBean对象的上一次操作者信息
runnerBeanOne.showLastOprBeanOfSingletonBean();
}
}
输出结果:
none
runnerBeanOne
none
runnerBeanOne
runnerBeanOne
在上边的代码中创建了两个SpringContainer对象 context 和 context2.分别用context和context2获取runnerBeanOne与runnerBeanTwo,再进行操作与输出,从结果来看他们分别应用的singletonBean对象同样是两个不同的对象。虽然在配置文件中两个runnerBean都引用了同一个singletonBean定义,但是因为是通过不同的SpringContainer创建的,所以仍然创建除了两个singletonBean对象这就可以理解第一点:1.每个Spring Container中定义的Bean只存在一个实例。
Spring默认是以上述单例模式创建对象的,但是通过Bean标签中的设置scope="prototype"可以设置Spring不再以单例模式创建对象,而是每次都创建新的对象。在上边的例子中,如果不再定义两个runnerBean,而是指定义一个runnerBean,但是给他设置属性scope=“prototype”,那在main方法中就可以通过同一个context获取两个不同的runnerBean对象。但是需要注意的是,因为runnerBean引用的singletonBean没有被设置为prototype,所以虽然创建除了两个不同的runnerBean对象,但他们所引用的singletonBean仍然是同一个。
所以单例模式在Spring中的使用需要额外的注意。
所有代码:
main方法类:
package beanscope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanScopceMain
{
/**
* @comment [注释说明]
* @author 荣磊, 2012-7-17
*
* @param args
*/
public static void main(String[] args)
{
String springConfig = "beanscope/spring-config.xml";
//创建一个SpringContainer
ApplicationContext context = new ClassPathXmlApplicationContext(springConfig);
//通过SpringContainer取得runnerBeanOne
RunnerBeanRefToSingletonBean runnerBeanOne =
context.getBean("runnerBeanOne", RunnerBeanRefToSingletonBean.class);
//通过SpringContainer取得runnerBeanTwo
RunnerBeanRefToSingletonBeanTwo runnerBeanTwo =
context.getBean("runnerBeanTwo", RunnerBeanRefToSingletonBeanTwo.class);
//通过SpringContainer取得singletonBean
SingletonBean singletonBean =
context.getBean("singletonBean", SingletonBean.class);
//输出上一次操作singltonBean的Bean名称,
//因为在这之前没有对singletonBean的lastOperatedBy属性进行初始化,
//所以这里应该会输出none
singletonBean.showLastOperateBean();
//设置runnerBeanOne中singletonBean对象的上一次操作者信息为runnerBeanOne
runnerBeanOne.setMyNameToSingletonBean();
//通过singletonBean输出上一次操作者信息
singletonBean.showLastOperateBean();
//输出runnerBeanTwo中应用的singletonBean对象的上一次操作者信息
runnerBeanTwo.showLastOprBeanOfSingletonBean();
//设置runnerBeanOne中singletonBean对象的上一次操作者信息为runnerBeanTwo
runnerBeanTwo.setMyNameToSingletonBean();
//通过singletonBean输出上一次操作者信息
singletonBean.showLastOperateBean();
//输出runnerBeanOne中应用的singletonBean对象的上一次操作者信息
runnerBeanOne.showLastOprBeanOfSingletonBean();
}
}
SingletonBean类:
package beanscope;
/**
* [功能描述]
*
* @author 荣磊
* @version 1.0, 2012-7-17
*/
public class SingletonBean
{
// 用来保存上次操作此Bean的对象
private String lastOperatedBy;
public String getLastOperatedBy()
{
return lastOperatedBy;
}
public void setLastOperatedBy(String lastOperatedBy)
{
this.lastOperatedBy = lastOperatedBy;
}
/**
* [输出lastOperatedBy属性]
* @author 荣磊, 2012-7-17
*/
public void showLastOperateBean()
{
if(lastOperatedBy!=null)
System.out.println(lastOperatedBy);
else
System.out.println("none");
}
}
RunnerBeanRefToSingletonBean类:
package beanscope;
public class RunnerBeanRefToSingletonBean
{
private String beanName;
private SingletonBean singletonBean;
public String getBeanName()
{
return beanName;
}
public void setBeanName(String beanName)
{
this.beanName = beanName;
}
public SingletonBean getSingletonBean()
{
return singletonBean;
}
public void setSingletonBean(SingletonBean singletonBean)
{
this.singletonBean = singletonBean;
}
public void setMyNameToSingletonBean()
{
singletonBean.setLastOperatedBy(beanName);
}
public void showLastOprBeanOfSingletonBean()
{
singletonBean.showLastOperateBean();
}
}
RunnerBeanRefToSingletonBeanTwo类:
package beanscope;
public class RunnerBeanRefToSingletonBeanTwo
{
private String beanName;
private SingletonBean singletonBean;
public String getBeanName()
{
return beanName;
}
public void setBeanName(String beanName)
{
this.beanName = beanName;
}
public SingletonBean getSingletonBean()
{
return singletonBean;
}
public void setSingletonBean(SingletonBean singletonBean)
{
this.singletonBean = singletonBean;
}
public void setMyNameToSingletonBean()
{
singletonBean.setLastOperatedBy(beanName);
}
public void showLastOprBeanOfSingletonBean()
{
singletonBean.showLastOperateBean();
}
}
spring-config.xml