注意spring, singleton不single! ?
Posted on 2010-08-30 00:41 瘋狂 閱讀(6915) 評論(12) 編輯 收藏 所屬分類: java 、spring 、web spring的singleton真的會在整個應用中創建單一的對象嗎?非也,他只會在內部springframework的一個applicationContext上下文中保持單利,但是如果有兩個applicationContext呢?
首先看以下實例 :
首先定義一個bean 并注冊為單利,非懶加載對象:
package com.joe.service;


public class TestService {

public TestService(){
System.out.println("create TestService
");
}
}
配置:
測試代碼:
結果:
create TestService 
create TestService 

context1 中 testservice 是否單利?true
com.joe.service.TestService@54c4ad
context2 中 testservice 是否單利?true
com.joe.service.TestService@13c7378
com.joe.service.TestService@54c4ad
context1 中 testservice 是否和context2 中 testservice 是一個對象?false
很明顯在一個jvm應用中出現兩個被標注為單利的實例。
spring內部通過上下文中的集合來管理對象,核心為beanFactory,而非通過asm等來動態改變類結構來控制類的scope。
我們跟著源碼來看看:
在調用new ClassPathXmlApplicationContext實際上調用了
this(configLocations, true, null);其中的true代表要刷新上下文:而最終的refresh是這樣的
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
但是很費解的是hasBeanFactory的判斷:
protected final boolean hasBeanFactory() {
synchronized (this.beanFactoryMonitor) {
return (this.beanFactory != null);
}
}
其中的this.beanFactory是ClassPathXmlApplicationContext的實例的屬性private DefaultListableBeanFactory beanFactory;
不用說this.beanFactory==null因為我們new了一個ClassPathXmlApplicationContext , beanFactory也沒有初始化操作,也沒有parentContext (因為我根本不知道有沒有parentContext)而beanFactory也不是static的。
也就是說在第二次new ClassPathXmlApplicationContext的時候并不會refresh beanFactory 當然就會有兩個單利的實例。
此問題在項目中出現,當時由于一個同事在自己的一個類里面的static區域new了ClassPathXmlApplicationContext用來測試忘記刪掉, 而我們的所有類又是通過web容器來加載,結果就導致項目中出現單利對象出現兩個的問題,結果害的那個苦?。?br />
希望大家在實際項目中注意到,也希望大家跟帖討論下,我分析的是否正確,大家實際項目中有沒有解決方案來避免這樣的問題!
首先看以下實例 :
首先定義一個bean 并注冊為單利,非懶加載對象:











配置:
<bean id="testservice" class="com.joe.service.TestService" scope="singleton" lazy-init="false"></bean>
測試代碼:
private static ApplicationContext context1;
private static ApplicationContext context2;
static{
context1 = new ClassPathXmlApplicationContext(new String[]{"classpath:spring-bean_*.xml"});
context2 = new ClassPathXmlApplicationContext(new String[]{"classpath:spring-bean_*.xml"});
}
public static void main(String[] args) throws SQLException, IOException {
System.out.println("context1 中 testservice 是否單利?"+context1.isSingleton("testservice"));
TestService service = (TestService) context1.getBean("testservice");
System.out.println(service);
System.out.println("context2 中 testservice 是否單利?"+context2.isSingleton("testservice"));
TestService service2 = (TestService) context2.getBean("testservice");
System.out.println(service2);
System.out.println(service);
System.out.println("context1 中 testservice 是否和context2 中 testservice 是一個對象?"+(service==service2));//關鍵地方 兩個ApplicationContext 中拿 到的是否是一個對象?
}
private static ApplicationContext context2;
static{
context1 = new ClassPathXmlApplicationContext(new String[]{"classpath:spring-bean_*.xml"});
context2 = new ClassPathXmlApplicationContext(new String[]{"classpath:spring-bean_*.xml"});
}
public static void main(String[] args) throws SQLException, IOException {
System.out.println("context1 中 testservice 是否單利?"+context1.isSingleton("testservice"));
TestService service = (TestService) context1.getBean("testservice");
System.out.println(service);
System.out.println("context2 中 testservice 是否單利?"+context2.isSingleton("testservice"));
TestService service2 = (TestService) context2.getBean("testservice");
System.out.println(service2);
System.out.println(service);
System.out.println("context1 中 testservice 是否和context2 中 testservice 是一個對象?"+(service==service2));//關鍵地方 兩個ApplicationContext 中拿 到的是否是一個對象?
}
結果:












很明顯在一個jvm應用中出現兩個被標注為單利的實例。
spring內部通過上下文中的集合來管理對象,核心為beanFactory,而非通過asm等來動態改變類結構來控制類的scope。
我們跟著源碼來看看:
在調用new ClassPathXmlApplicationContext實際上調用了
this(configLocations, true, null);其中的true代表要刷新上下文:而最終的refresh是這樣的




但是很費解的是hasBeanFactory的判斷:





其中的this.beanFactory是ClassPathXmlApplicationContext的實例的屬性private DefaultListableBeanFactory beanFactory;
不用說this.beanFactory==null因為我們new了一個ClassPathXmlApplicationContext , beanFactory也沒有初始化操作,也沒有parentContext (因為我根本不知道有沒有parentContext)而beanFactory也不是static的。
也就是說在第二次new ClassPathXmlApplicationContext的時候并不會refresh beanFactory 當然就會有兩個單利的實例。
此問題在項目中出現,當時由于一個同事在自己的一個類里面的static區域new了ClassPathXmlApplicationContext用來測試忘記刪掉, 而我們的所有類又是通過web容器來加載,結果就導致項目中出現單利對象出現兩個的問題,結果害的那個苦?。?br />
希望大家在實際項目中注意到,也希望大家跟帖討論下,我分析的是否正確,大家實際項目中有沒有解決方案來避免這樣的問題!