在我的程序生涯中使用单例模式用的次数和工厂模式差不多,但在Spring中,使用的次数会更多。因为,在Spring里面,一切皆是单例。当你在Spring中创建一个Bean时,默认就是一个单例,除非你跟我们的老朋友Application Context特别要求,不然你就得到一个单例的Bean。那么有些朋友会问我,什么是特别要求,我在接下来的系列中提到Prototype模式时,会提到这个特别要求。值得一提的是,接下来我会介绍传统的单例模式,这个跟Spring的实现有所不同,但是原理是一样的。
[WHY]首先我们说说为什么要有这样的模式,它能解决什么问题?
我总结下来有以下三点
- Expensive object creation
- Control concurrency associated shared resources
- Storing static state for multiple parts of the application
首先谈谈第一点 – Expensive object creation,在实际工作中,大家往往要构建一些占用大量的i/o 或memory的object,比如我们熟悉的database connection object,remote call的那些client object (用过Apache CXF的同学应该比较熟悉)。这些object建造起来往往花费大量时间,但是又易于重复使用。因此,聪明的同学们应该猜到,只要结合单例模式和建一个线程池(pool)就能解决这个问题。相信大家应该有点感觉,这个就是传统的jdbc pool的解决方案。
第二点原因则是共享的问题,Web Application是一个高并发的例子,比如所有requests共享的web context,上面提到的database connection也是一个共享资源。那么原理很简单,要共享,那就只有一个 – 也就是单例
第三点则是你需要控制Application全局的状态。比如,当前比较流行的single page application,一个页面有n个components,那么如果这些components拥有一些共享的状态,比如,这些component有一个user的属性,当你log in后,所有components都share这个state,除非你用另外一个user,那么所有components 的state都会变,很明显,这个state就是一个单例。
[HOW]那么下面我来谈谈如何实现?
我们可爱的bean,因为它们是单例,因为它们必须是thread safe的。因而,这个模式非常容易理解,首先你有一个class,这个class存储着一个reference指向自己,或者说,一个自己的instance。然后,你需要有一个private constructor,然后你有一个static getinstance method 去返回自己, 把constructor私有化使得你在getInstance method用不了new method去构建新的instance。由于多线程同时共享同一个instance,那么你在调用时必须引入一些locking的logic,比如synchronized block去保证unsafe的情况。
接下来的代码来自于我工作中的一段代码,这段代码的作用是构建出一个调用SOAP Webservice的service client,当中利用一个object pool和单例模式。
public class OrderWSClientFacade {
private static volatile Service orderService = null;
private static ObjectPool<OrderWSPortHolder> orderServicePortPool;
public OrderWSClientFacade(String serviceLocation) throws MalformedURLException {
//check before multiple threads acquire lock
if(orderService == null){
synchronized(this) {
//check before actual port initialization once, one of the thread gets access to this block
if(orderService == null){
try
{
URL url = new URL(serviceLocation);
orderService = new OrderCWS(url);
GenericObjectPoolConfig orderServicePortPoolConfig = new GenericObjectPoolConfig();
orderServicePortPool = new GenericObjectPool<OrderWSPortHolder>( new OrderServicePortFactory(),orderServicePortPoolConfig); // initialize the Pool ONLY once !!
// ignore some lines of code of configuration
}
catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}