关于Spring创建Bean的模式-Singleton(单例模式)和Prototype

Spring Bean的创建:默认情况下Spring中定义的Bean是以单例模式创建的。
在GoF中的单例模式是指一个ClassLoader中只存在类一个实例。
而在Spring中的单例实际上更确切的说应该是:
1.每个Spring Container中定义的Bean只存在一个实例
2.每个Bean定义只存在一个实例。

如果对Spring的单例模式不够了解在设计与开发过程中会留下很多隐患。以下是根据Spring中单例模式的特点写的测试代码,通过代码可以更直观的了解Spring中的单例模式。

以下main方法进行测试:

[java]
package beanscope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class BeanScopceMain
{
    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是相同的。如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="
http://www.springframework.org/schema/beans"
    xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans 
       
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 
    <bean id="runnerBeanOne" class="beanscope.RunnerBeanRefToSingletonBean">
        <property name="beanName" value="runnerBeanOne" />
        <property name="singletonBean" ref="singletonBean" />
    </bean> 
www.2cto.com
    
    <bean id="runnerBeanTwo" class="beanscope.RunnerBeanRefToSingletonBeanTwo">
        <property name="beanName" value="runnerBeanTwo" />
        <!-- 这里更改了ref的值为singletonBeanTwo -->
        <property name="singletonBean" ref="singletonBeanTwo" />
    </bean>
    
    <bean id="singletonBean" class="beanscope.SingletonBean">
       <property name="lastOperatedBy">
           <null />
       </property>
    </bean>
    <!-- 增加了以下Bean定义 -->
    <bean id="singletonBeanTwo" class="beanscope.SingletonBean">
       <property name="lastOperatedBy">
           <null />
       </property>
    </bean>
</beans>

更改配置文件后执行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
{
    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仍然是同一个。

<bean id="runnerBeanOne" class="beanscope.RunnerBeanRefToSingletonBean" 
    scope="prototype">
    <property name="beanName" value="runnerBeanOne" />
    <property name="singletonBean" ref="singletonBean" />
</bean>
 
<bean id="singletonBean" class="beanscope.SingletonBean">
    <property name="lastOperatedBy">
       <null />
    </property>
</bean>

所以单例模式在Spring中的使用需要额外的注意。
 

所有代码:

package beanscope;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class BeanScopceMain
{
    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;
public class SingletonBean
{
    // 用来保存上次操作此Bean的对象
    private String lastOperatedBy;
    public String getLastOperatedBy()
    {
        return lastOperatedBy;
    }
    public void setLastOperatedBy(String lastOperatedBy)
    {
        this.lastOperatedBy = lastOperatedBy;
    }
    
    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类:

[java]
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

[html]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="
http://www.springframework.org/schema/beans"
    xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans 
       
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 
    <bean id="runnerBeanOne" class="beanscope.RunnerBeanRefToSingletonBean">
        <property name="beanName" value="runnerBeanOne" />
        <property name="singletonBean" ref="singletonBean" />
    </bean>
    
    <bean id="runnerBeanTwo" class="beanscope.RunnerBeanRefToSingletonBeanTwo">
        <property name="beanName" value="runnerBeanTwo" />
        <property name="singletonBean" ref="singletonBean" />
    </bean>
    
    <bean id="singletonBean" class="beanscope.SingletonBean">
       <property name="lastOperatedBy">
           <null />
       </property>
    </bean>
    
</beans>

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值