Spring中常见的设计模式——单例模式

一、单例模式的应用场景

  单例模式(singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。J2EE中的ServletContext,ServletContextConfig等;Spring中的ApplicationContext、数据库连接池等。

二、饿汉式单例模式

  饿汉式单例模式在类加载的时候就立即初始化,并且创建单例对象。它是绝对的线程安全、在线程还没出现以前就实现了,不可能存在访问安全问题。

  优点:没有增加任何锁,执行效率高,用户体验比懒汉式好。

  缺点:类加载的时候就初始化了,用不用都进行,浪费内存。

  Spring 中IoC容器ApplocationContext本身就是典型的饿汉式单例模式:

public class HungrySingleton {
    private static final HungrySingleton h = new HungrySingleton();

    private HungrySingleton() {
    }

    public static HungrySingleton getInstance() {
        return h;
    }
}

  饿汉式单例模式适用于单例对象较少的情况。

三、懒汉式单例模式

  被外部调用才会加载:

public class LazySimpleSingleton {
    private LazySimpleSingleton() {
    }

    private static LazySimpleSingleton lazy = null;

    public static LazySimpleSingleton getInstance() {
        if (lazy == null) {
            lazy = new LazySimpleSingleton();
        }
        return lazy;
    }
}

利用线程创建实例:

public class ExectorThread implements Runnable {
    @Override
    public void run() {
        LazySimpleSingleton simpleSingleton = LazySimpleSingleton.getInstance();
        System.out.println(Thread.currentThread().getName() + ":" + simpleSingleton);
    }
}

客户端代码:

public class LazySimpleSingletonTest {
    public static void main(String[] args) {
        Thread t1 = new Thread(new ExectorThread());
        Thread t2 = new Thread(new ExectorThread());
        t1.start();
        t2.start();
        System.out.println("END");
    }
}

结果:

END
Thread-1:[email protected]
Thread-0:[email protected]

可以看到 产生的两个实例的内存地址不同说明产生了两个实例,大家可以通过以下打断点的方式实现不同Thread运行状态见进行切换。

  要解决线程问题第一反应是加 synchronized 加在创建实例的地方:public static synchronized LazySimpleSingleton getInstance(),但当线程数量较多时,用Synchronized加锁,会使大量线程阻塞,就需要更好的解决办法:

    public static LazySimpleSingleton getInstance() {
        if (lazy == null) {
            synchronized (LazySimpleSingleton.class) {
                if (lazy == null) {
                    lazy = new LazySimpleSingleton();
                }
            }
        }
        return lazy;
    }

  synchronized (lock) lock这个对象就是 “锁”,当两个并行的线程a,b,当a先进入同步块,即a先拿到lock对象,这时候a就相当于用一把锁把synchronized里面的代码锁住了,现在只有a才能执行这块代码,而b就只能等待a用完了lock对象锁之后才能进入同步块。但是用到 synchronized 总归是要上锁的,对性能还是有影响,那就用这种方式:用内部类的方式进行懒加载。

public class LazyInnerClassSingleton {
    private LazyInnerClassSingleton() {
    }

    private static final LazyInnerClassSingleton getIngestance() {
        return LazyHolder.LAZY;
    }

    private static class LazyHolder {
        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
    }
}

  内部类在LazyInnerClassSingleton类加载时加载,解决了饿汉式的性能问题,LazyInnerClassSingleton在内部类加载时,getIngestance()方法被调用之前实例化,解决了线程不安全问题。

四、反射破坏单例

public class LazyInnerClassSingletonTest {
    public static void main(String[] args) {
        try {
            Class<?> clazz = LazyInnerClassSingleton.class;
            //通过反射回去私有构造方法
            Constructor constructor = clazz.getDeclaredConstructor(null);
            //强制访问
            constructor.setAccessible(true);
            //暴力初始化
            Object o1 = constructor.newInstance();
            //创建两个实例
            Object o2 = constructor.newInstance();
            System.out.println("o1:" + o1);
            System.out.println("o2:" + o2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

结果:

o1:[email protected]
o2:[email protected]

创建了两个实例,违反了单例,现在在构造方法中做一些限制,使得多次重复创建时,抛出异常:

 private LazyInnerClassSingleton() {
        if (LazyHolder.class != null) {
            throw new RuntimeException("不允许创建多个实例");
        }
    }

这应该就是最好的单例了,哈哈哈。

五、注册式单例模式

  注册式单例模式又称为登记式单例模式,就是将每个实例都登记到某个地方,使用唯一标识获取实例。注册式单例模式有两种:枚举式单例模式、容器式单例模式。注册式单例模式主要解决通过反序列化破坏单例模式的情况。

1.枚举式单例模式 

public enum EnumSingleton {
    INSTANCE;
    private Object data;

    public Object getData() {
        return data;
    }

    public void steData(Object data) {
        this.data = data;
    }

    public static EnumSingleton getInstance() {
        return INSTANCE;
    }
}

测试代码:

public class EnumSingletonTest {
    public static void main(String[] args) {
        try {
            EnumSingleton instance1 = EnumSingleton.getInstance();
            EnumSingleton instance2 = null;
            instance1.steData(new Object());

            FileOutputStream fos = new FileOutputStream("EnumSingleton.obj");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(instance1);
            oos.flush();
            oos.close();

            FileInputStream fis = new FileInputStream("EnumSingleton.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            instance2 = (EnumSingleton) ois.readObject();
            ois.close();

            System.out.println(instance1.getData());
            System.out.println(instance2.getData());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

结果:

[email protected]
[email protected]

  那枚举式单例是如何解决反序列化得问题呢?

  通过反编译,可以在EnumSingleton.jad文件中发现static{} 代码块,枚举式单例模式在静态代码块中给INSTANCE进行了赋值,是饿汉式单例模式的实现。查看JDK源码可知,枚举类型其实通过类名和类对象找到一个唯一的枚举对象。因此,枚举对象不可能被类加载器加载多次。

  当你试图用反射破坏单例时,会报 Cannot reflectively create enum objects ,即不能用反射来创建枚举类型。进入Customer的newInstance(),其中有判断:如果修饰符是Modifier.ENUM,则直接抛出异常。JDK枚举的语法特殊性及反射也为美剧保驾护航,让枚举式单例模式成为一种比较优雅的实现。

2.容器式单例

public class ContainerSingleton {
    private ContainerSingleton() {
    }

    private static Map<String, Object> ioc = new ConcurrentHashMap<>();

    public static Object getBean(String className) {
        synchronized (ioc) {
            if (!ioc.containsKey(className)) {
                Object o = null;
                try {
                    o = Class.forName(className).newInstance();
                    ioc.put(className, o);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return o;
            } else {
                return ioc.get(className);
            }
        }
    }
}

spring中使用的就是容器式单例模式。

  

原文地址:https://www.cnblogs.com/xcgShare/p/11951891.html

时间: 2024-08-28 09:20:45

Spring中常见的设计模式——单例模式的相关文章

Spring中常见的设计模式——装饰者模式

一.装饰者模式的应用场景 装饰者模式(Decorator Pattern)是指在不改变原有对象的基础上,将功能附加到对象上,提供了比继承更有弹性的方案(扩展原有对象的功能),属于结构型模式.装饰者模式适用于以下场景: 扩展一个类的功能或给一个类添加附加职责. 动态给一个对象天机功能,这些功能可以再动态的撤销. 以做煎饼果子为例演示装饰者模式是怎么装的: 首先做一个煎饼果子抽象: public abstract class Battercake { protected abstract Strin

Spring中常见的bean创建异常

Spring中常见的bean创建异常 1. 概述     本次我们将讨论在spring中BeanFactory创建bean实例时经常遇到的异常 org.springframework.beans.factory.BeanCreationException,下面我们将讨论并再现这些异常,同时给出解决方案. 2. Cause:org.springframework.beans.factory.NoSuchBeanDefinitionException     到目前为止最常见的导致BeanCreat

Java中常见的设计模式

我在这里稍微总结一下Java中常见的一些设计模式. 1,单例设计模式 单例设计是Java中最常见的模式之一,,它就是要保证程序里边只有一个对象实例.就好比班上都叫他为班长,是因为得保证班上只有一个班长. 单例设计模式的程序设计思想反应的是Java面向对象思想的封装特性.就是为了保证只有一个对象实例,我们私有化它的构造函数,对外只提供一些相关的 公共方法来获得对象实例. 单例设计模式的实现主要分为饿汉式和懒汉式 饿汉式是先对类的对象实例化,随着类的加载对象也加载进来缺点是会造成性能的损耗好处是实现

Android开发中常见的设计模式(一)——单例模式

首先了解一些单例模式的概念. 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 这样做有以下几个优点 对于那些比较耗内存的类,只实例化一次可以大大提高性能,尤其是在移动开发中. 保持程序运行的时候该中始终只有一个实例存在内存中 其实单例有很多种实现方式,但是个人比较倾向于其中1种.可以见单例模式 代码如下 public class Singleton { private static volatile Singleton instance = null; private Sin

Android中常见的设计模式

自己理解的设计模式遵循的原则: 1)功能单一明确,设计一个类的意图要明确,不能大包大揽什么功能都继承进去 2)对于扩展要开放,修改要关闭.软件通常都有需求变化,变化过程中通过扩展的方式来实现需求变化,而不是通过修改原有的方法,因为修改原有的方法会导致原来方法的调用方会出问题,这样层层调用出问题. 3)变化的进行抽象,不变的进行具体.设计代码过程中会面对很对可变的东西,比如在实现一个功能的时候,能够运用不同的方式进行实现,这个时候可以将每个具体的实现方法进行抽象,真正不变的是这个方法要实现的目的

Android开发中常见的设计模式

对于开发人员来说,设计模式有时候就是一道坎,但是设计模式又非常有用,过了这道坎,它可以让你水平提高一个档次.而在android开发中,必要的了解一些设计模式又是非常有必要的.对于想系统的学习设计模式的同学,这里推荐2本书.一本是Head First系列的Head Hirst Design Pattern,英文好的可以看英文,可以多读几遍.另外一本是大话设计模式. 单例模式 首先了解一些单例模式的概念. 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 这样做有以下几个优点 对于

IOS开发中常见的设计模式

(一)代理模式应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现.优势:解耦合敏捷原则:开放-封闭原则实例:tableview的 数据源delegate,通过和protocol的配合,完成委托诉求.列表row个数delegate自定义的delegate (二)观察者模式应用场景:一般为model层对,controller和view进行的通知方式,不关心谁去接收,只负责发布信息.优势:解耦合敏捷原则:接口隔离原则,开放-封闭原则实例:Notification通知中心,注

Android开发中无处不在的设计模式——单例模式

对于开发人员来说,设计模式有时候就是一道坎,但是设计模式又非常有用,过了这道坎,它可以让你水平提高一个档次.而在android开发中,必要的了解一些设计模式又是非常有必要的.对于想系统的学习设计模式的同学,这里推荐2本书.一本是Head First系列的Head Hirst Design Pattern,英文好的可以看英文,可以多读几遍.另外一本是大话设计模式. 这篇文章介绍一个模式,就是单例模式,因为个人觉得这个模式理解起来最容易,而且不是太复杂. 首先了解一些什么是单例,从名字中就可以听出来

iOS开发——高级篇——iOS中常见的设计模式(MVC/单例/委托/观察者)

关于设计模式这个问题,在网上也找过一些资料,下面是我自己总结的,分享给大家 如果你刚接触设计模式,我们有好消息告诉你!首先,多亏了Cocoa的构建方式,你已经使用了许多的设计模式以及被鼓励的最佳实践. 首先得搞清楚设计模式是个什么鬼,在软件设计领域,设计模式是对通用问题的可复用的解决方案.设计模式是一系列帮你写出更可理解和复用代码的模板,设计模式帮你创建松耦合的代码以便你不需要费多大力就可以改变或者替换代码中的组件 其实iOS中的设计模式有非常多,常用的就下面这四种 一.MVC设计模式(设计模式