基本定义
通过上面简单介绍,我们可以对单例模式有一个简单的认识。所谓单例模式就是确保某一个类只有一个实例,并且提供一个全局访问点。
从上面可以看出单例模式有如下几个特点:
-
一、它只有一个实例。
-
二、它必须要自行实例化。
-
三、它必须自行想整个系统提供访问点。
模式结构
单例模式可以说是最简单的设计模式了,它仅有一个角色Singleton。
- Singleton:单例。
模式实现
在看Spring单例应用之前,让我们来看一个Java的单例例子:
枚举方法实现单例
public class SingletonTest { @Test public void test() { President president1 = (President) SingletonsHolder.PRESIDENT.getHoldedObject(); President president2 = (President) SingletonsHolder.PRESIDENT.getHoldedObject(); assertTrue("Both references of President should point to the same object", president1 == president2); System.out.println("president1 = "+president1+" and president2 = "+president2); // sample output // president1 = com.waitingforcode.test.President@17414c8 and president2 = com.waitingforcode.test.President@17414c8 }}enum SingletonsHolder { PRESIDENT(new President()); private Object holdedObject; private SingletonsHolder(Object o) { this.holdedObject = o; } public Object getHoldedObject() { return this.holdedObject; }}class President {}
也可以用静态类内部加载的方式来实现单例
public class SingletonDemo { private static class SingletonHolder{ private static SingletonDemo instance=new SingletonDemo(); } private SingletonDemo(){ System.out.println("Singleton has loaded"); } public static SingletonDemo getInstance(){ return SingletonHolder.instance; }}
在Spring中,我们可以在bean工厂中找到单例应用的影子(例如在org.springframework.beans.factory.config.AbstractFactoryBean中):
/*** Expose the singleton instance or create a new prototype instance.* @see #createInstance()* @see #getEarlySingletonInterfaces()*/@Overridepublic final T getObject() throws Exception { if (isSingleton()) { return (this.initialized ? this.singletonInstance : getEarlySingletonInstance()); } else { return createInstance(); }}
我们看到,当需求对象被视为单例时,它只被初始化一次,并且在每次使用同一个bean类的实例后返回。我们可以在给定的例子中看到,类似于我们以前看到的President情况。将测试bean定义为:
测试用例如下所示:
public class SingletonSpringTest { @Test public void test() { // retreive two different contexts ApplicationContext firstContext = new FileSystemXmlApplicationContext("applicationContext-test.xml"); ApplicationContext secondContext = new FileSystemXmlApplicationContext("applicationContext-test.xml"); // prove that both contexts are loaded by the same class loader assertTrue("Class loaders for both contexts should be the same", firstContext.getClassLoader() == secondContext.getClassLoader()); // compare the objects from different contexts ShoppingCart firstShoppingCart = (ShoppingCart) firstContext.getBean("shoppingCart"); ShoppingCart secondShoppingCart = (ShoppingCart) secondContext.getBean("shoppingCart"); assertFalse("ShoppingCart instances got from different application context shouldn't be the same", firstShoppingCart == secondShoppingCart); // compare the objects from the same context ShoppingCart firstShoppingCartBis = (ShoppingCart) firstContext.getBean("shoppingCart"); assertTrue("ShoppingCart instances got from the same application context should be the same", firstShoppingCart == firstShoppingCartBis); }}
这个测试案例显示了Spring单例模式与纯粹的单例设计模式的主要区别。尽管使用相同的类加载器来加载两个应用程序上下文,但是ShoppingCart的实例是不一样的。但是,当我们比较两次创建并属于相同上下文的实例时,我们认为它们是相等的。
也正因为有了单例,Spring可以控制在每个应用程序上下文中只有一个这样指定的bean的实例可用。因为适配器,Spring可以决定使用由谁来处理 JBossservlet
容器中的加载时编织,也可以实现 ConfigurableListableBeanFactory
的相应实例。第三种设计模式,装饰器,用于向Cache对象添加同步功能,还有Springboot的容器初始化。
模式优缺点
优点
- 一、节约了系统资源。由于系统中只存在一个实例对象,对与一些需要频繁创建和销毁对象的系统而言,单
例模式无疑节约了系统资源和提高了系统的性能。
- 二、因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。
缺点
-
一、由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
-
二、单例类的职责过重,在一定程度上违背了“单一职责原则”。
模式使用场景
下列几种情况可以使用单例模式。
-
一、系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。
-
二、客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。