單例多例須要搞明白這些問題:
1. 什麼是單例多例;
2. 如何產生單例多例;
3. 爲何要用單例多例
4. 何時用單例,何時用多例;
1. 什麼是單例、多例:
所謂單例就是全部的請求都用一個對象來處理,好比咱們經常使用的service和dao層的對象一般都是單例的,而多例則指每一個請求用一個新的對象來處理,好比action;
單例模式和多例模式說明:
1. 單例模式和多例模式屬於對象模式。
2. 單例模式的對象在整個系統中只有一份,多例模式能夠有多個實例。
3. 它們都不對外提供構造方法,即構造方法都爲私有。
2. 如何產生單例、多例:
在通用的SSH中,單例在spring中是默認的,若是要產生多例,則在配置文件的bean中添加scope="prototype";
3. 爲何用單例、多例:
之因此用單例,是由於不必每一個請求都新建一個對象,這樣子既浪費CPU又浪費內存;
之因此用多例,是爲了防止併發問題;即一個請求改變了對象的狀態,此時對象又處理另外一個請求,而以前請求對對象狀態的改變致使了對象對另外一個請求作了錯誤的處理;
用單例和多例的標準只有一個:
當對象含有可改變的狀態時(更精確的說就是在實際應用中該狀態會改變),則多例,不然單例;
4. 什麼時候用單例?什麼時候用多例?
對於struts2來講,action必須用多例,由於action自己含有請求參數的值,便可改變的狀態;
而對於STRUTS1來講,action則可用單例,由於請求參數的值是放在actionForm中,而非action中的;
另外要說一下,並非說service或dao必定是單例,標準同第3點所講的,就曾見過有的service中也包含了可改變的狀態,同時執行方法也依賴該狀態,但同樣用的單例,這樣就會出現隱藏的BUG,而併發的BUG一般很難重現和查找;
spring生成對象默認是單例的。經過scope屬性能夠更改成多例
<bean id="user" class="modle.User" scope="prototype"> </bean>
如今又這麼一種狀況.
User類調用一個service, 這個service又調用一個tool。spring
有時咱們但願User是多例的,service是單例的,而tool又是多例的。併發
很天然地想法是配置文件這些寫
<bean id="user" class="modle.User" scope="prototype">
<property name="service" ref="userservice"></property>
</bean>
<bean id="userservice" class="service.userService" >
<property name="tool" ref="tool"></property>
</bean>
<bean id="tool" class="service.ToolImpl" scope="prototype">
</bean>
可是這種寫法是錯誤的! 不能使用spring的自動注入!
因爲service是單例的,因此這種方法的結果是:User多例,service和tool都是單例。(爲何?)
官網文檔:
4.5.3 Singleton beans with prototype-bean dependencies
When you use singleton-scoped beans with dependencies on prototype beans, be aware that dependencies are resolved at instantiation time. Thus if you dependency-inject a prototype-scoped bean into a singleton-scoped bean, a new prototype bean is instantiated and then dependency-injected into the singleton bean. The prototype instance is the sole instance that is ever supplied to the singleton-scoped bean.
However, suppose you want the singleton-scoped bean to acquire a new instance of the prototype-scoped bean repeatedly at runtime. You cannot dependency-inject a prototype-scoped bean into your singleton bean, because that injection occurs only once, when the Spring container is instantiating the singleton bean and resolving and injecting its dependencies. If you need a new instance of a prototype bean at runtime more than once, see Section 4.4.6, 「Method injection」
正確的寫法是,是每次調用tool時都生成一個新的tool對象。可是咱們又不能手動new一個,要藉助BeanFactory
public class User {
private userService service;
private int age;
private Date date;
private String name;
UserService 經過實現 BeanFactoryAware 接口來得到factory
因爲不使用spring的自動注入,set方法要去掉!
public
class userService implements BeanFactoryAware{
private Tool tool;
private BeanFactory factory;
public void service(){
this.tool = (Tool)factory.getBean("tool");
System.out.println(this+":service");
tool.work();
}
public Tool getTool() {
return tool;
}
// public void setTool(Tool tool) {
//
//
this.tool = (Tool)factory.getBean("tool");
// }
public void setBeanFactory(BeanFactory f) throws BeansException {
factory = f;
}
}
配置文件,不能再使用注入。所以要把tool對象的注入去掉!
<bean id="user" class="modle.User" scope="prototype">
<property name="service" ref="userservice"></property>
</bean>
<bean id="userservice" class="service.userService" >
</bean>
<bean id="tool" class="service.ToolImpl" scope="prototype">
</bean>
public
interface Tool {
public void work();
}
public
class ToolImpl implements Tool{
public void work() {
System.out.println(this+":Tool Work");
}
}
測試類:
public class Test {
public static void main(String[] args) {
ClassPathResource res = new ClassPathResource("applicationContext.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);
User user = (User)factory.getBean("user");
User user2 = (User)factory.getBean("user");
System.out.println(user);
user.getService().service();
System.out.println();
System.out.println(user2);
user2.getService().service();
}
}
Output:
modle.User@42552c
service.userService@19e15c:service
service.ToolImpl@11a75a2:Tool Work
modle.User@210b5b service.userService@19e15c:service service.ToolImpl@170888e:Tool Work