一、概述

  保证一个类仅有一个实例,并提供一个全局访问点

线程安全、延迟加载、序列化和反序列化安全、反射攻击

1.1、适用场景

  1、在多个线程之间,比如servlet环境,共享同一个资源或者操作同一个对象。

  2、在整个程序空间使用全局变量,共享资源。

  3、在大规模系统中,为了性能的考虑,需要节省对象的创建时间等等。

  想确保任何情况下都绝对只有一个实例

1.2、优缺点

单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:

在内存里只有一个实例,减少了内存开销,可以避免对资源的多重占用

  2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。

  3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。

缺点:

  1、没有接口,扩展困难

1.3、两种创建模式对比  

  饿汉式是典型的空间换时间,当类装载的时候就会创建类实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断了,节省了运行时间。

  懒汉式是典型的时间换空间,延迟加载,也就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间。

二、详细说明

  小结:一般情况下,推荐使用饿汉方式, 只有在要明确实现 lazy loading 效果时,才会使用静态内置类方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用双检锁方式。

2.1、饿汉模式

是否 Lazy 初始化:

是否多线程安全:

实现难度:

描述:这种方式比较常用,但容易产生垃圾对象。

优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。

  它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。

  它的特点是加载类的时候比较慢,但运行时获得对象的速度比较快。它从加载到应用结束会一直占用资源。程序初始化的时候初始化单例。



public class EagerSingleton {
    //饿汉单例模式
    //在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快
    private static EagerSingleton instance = new EagerSingleton();//静态私有成员,已初始化

    private EagerSingleton() {
        //私有构造函数
        System.out.println("new EagerSingleton");
    }

    //静态,不用同步(类加载时已初始化,不会有多线程的问题)
    public static EagerSingleton getInstance() {
        return instance;
    }
}



测试



@Test
    public void getInstance() {
        long start = System.currentTimeMillis();
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(100);
//        Set<Integer> setObj = new HashSet<>(); //线程并发问题
        ConcurrentSkipListSet<Integer> setObj = new ConcurrentSkipListSet();
        for (int i = 0; i < 100; i++) {
            fixedThreadPool.submit(() -> {
                EagerSingleton instance = EagerSingleton.getInstance();
                setObj.add(instance.hashCode());
            });
        }
        fixedThreadPool.shutdown();
        System.out.println("耗时:"+(System.currentTimeMillis()-start)+"ms");
        System.out.println("生成类数:"+setObj.size());
        for (Integer s : setObj) {
            System.out.println("hashcode:"+s);
        }
    }



结果



new EagerSingleton
耗时:73ms
生成类数:1
hashcode:1512308980



2.2、懒汉模式【后创建】  

  它的特点是运行时获得对象的速度比较慢,但加载类的时候比较快。它在整个应用的生命周期只有一部分时间在占用资源。

2.2.1、方式一、synchronized锁机制

版本一、初始版本【不可以,没有加锁】

是否 Lazy 初始化:

是否多线程安全:

实现难度:

描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。

这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。




spring 手动创建消息队列 spring创建单例_spring 手动创建消息队列

spring 手动创建消息队列 spring创建单例_runtime_02

public class Singleton001 {

    /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
    private static Singleton001 instance = null;

    /* 私有构造方法,防止被实例化 */
    private Singleton001() {
        System.out.println("new Singleton001");
    }

    /* 静态工程方法,创建实例 */
    public static Singleton001 getInstance() {
        if (instance == null) {
            instance = new Singleton001();
        }
        return instance;
    }

    /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
    public Object readResolve() {
        return instance;
    }
}


View Code


  会有多线程问题。

测试




spring 手动创建消息队列 spring创建单例_spring 手动创建消息队列

spring 手动创建消息队列 spring创建单例_runtime_02

@Test
    public void getInstance() {
        long start = System.currentTimeMillis();
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(100);
//        Set<Integer> setObj = new HashSet<>(); //线程并发问题
        ConcurrentSkipListSet<Integer> setObj = new ConcurrentSkipListSet();
        for (int i = 0; i < 100; i++) {
            fixedThreadPool.submit(() -> {
                Singleton001 instance = Singleton001.getInstance();
                setObj.add(instance.hashCode());
            });
        }
        fixedThreadPool.shutdown();
        System.out.println("耗时:"+(System.currentTimeMillis()-start)+"ms");
        System.out.println("生成类数:"+setObj.size());
        for (Integer s : setObj) {
            System.out.println("hashcode:"+s);
        }
    }


View Code


输出



new Singleton001
new Singleton001
new Singleton001
new Singleton001
耗时:72ms
生成类数:4
hashcode:158094720
hashcode:1046297923
hashcode:1303744419
hashcode:1572373644



版本二、synchronized同步方法迭代【可以,但是效率比较低,不推荐】

 

是否 Lazy 初始化:

 

是否多线程安全:

 

实现难度:

描述:这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。

优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。

getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。

 



public class Singleton002 {
    
    /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
    private static Singleton002 instance = null;

    /* 私有构造方法,防止被实例化 */
    private Singleton002() {
    }

    /* 静态工程方法,创建实例 */
    public static synchronized Singleton002 getInstance() {
        if (instance == null) {
            instance = new Singleton002();
        }
        return instance;
    }

    /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
    public Object readResolve() {
        return instance;
    }
}



但是,synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降,因为每次调用getInstance(),都要对对象上锁,

测试:



@Test
    public void getInstance() {
        long start = System.currentTimeMillis();
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(50);
//        Set<Integer> setObj = new HashSet<>(); //线程并发问题
        ConcurrentSkipListSet<Integer> setObj = new ConcurrentSkipListSet();
        for (int i = 0; i < 50; i++) {
            fixedThreadPool.submit(() -> {
                Singleton002 instance = Singleton002.getInstance();
                setObj.add(instance.hashCode());
            });
        }
        fixedThreadPool.shutdown();
        System.out.println("耗时:"+(System.currentTimeMillis()-start)+"ms");
        System.out.println("生成类数:"+setObj.size());
        for (Integer s : setObj) {
            System.out.println("hashcode:"+s);
        }
    }



输出



new Singleton002
耗时:67ms
生成类数:1
hashcode:1052225082



这种实现方式的运行效率会很低。同步方法效率低。

版本三、同步代码块synchronized迭代【不可以,创建和赋值是两步操作,没锁住】

事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了,所以,这个地方需要改进。我们改成下面这个:




spring 手动创建消息队列 spring创建单例_spring 手动创建消息队列

spring 手动创建消息队列 spring创建单例_runtime_02

public class Singleton003 {

    /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
    private static Singleton003 instance = null;

    /* 私有构造方法,防止被实例化 */
    private Singleton003() {
        System.out.println("new Singleton003");
    }

    /* 静态工程方法,创建实例 */
    public static Singleton003 getInstance() {
        if (instance == null) {
            synchronized (Singleton003.class) {
                instance = new Singleton003();//
            }
        }
        return instance;
    }

    /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
    public Object readResolve() {
        return instance;
    }
}


View Code


测试




spring 手动创建消息队列 spring创建单例_spring 手动创建消息队列

spring 手动创建消息队列 spring创建单例_runtime_02

@Test
    public void getInstance() {
        long start = System.currentTimeMillis();
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(500);
//        Set<Integer> setObj = new HashSet<>(); //线程并发问题
        ConcurrentSkipListSet<Integer> setObj = new ConcurrentSkipListSet();
        for (int i = 0; i < 500; i++) {
            fixedThreadPool.submit(() -> {
                Singleton003 instance = Singleton003.getInstance();
                setObj.add(instance.hashCode());
            });
        }
        fixedThreadPool.shutdown();
        System.out.println("耗时:"+(System.currentTimeMillis()-start)+"ms");
        System.out.println("生成类数:"+setObj.size());
        for (Integer s : setObj) {
            System.out.println("hashcode:"+s);
        }
    }


View Code


输出



new Singleton003
new Singleton003
耗时:68ms
生成类数:2
hashcode:158094720
hashcode:1961726163



这样的方法进行代码块同步,代码的运行效率是能够得到提升,但是却没能保住线程的安全性。

在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。

版本四、同步代码块synchronized迭代,增加一次判断【可以】



public class Singleton004 {

    /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
    private static Singleton004 instance = null;

    /* 私有构造方法,防止被实例化 */
    private Singleton004() {
        System.out.println("new Singleton004");
    }

    /* 静态工程方法,创建实例 */
    public static Singleton004 getInstance() {
        if (instance == null) {
            synchronized (Singleton004.class) {
                if (instance == null) {
                    instance = new Singleton004();
                }
            }
        }
        return instance;
    }

    /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
    public Object readResolve() {
        return instance;
    }
}



测试



@Test
    public void getInstance() {
        long start = System.currentTimeMillis();
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(50);
//        Set<Integer> setObj = new HashSet<>(); //线程并发问题
        ConcurrentSkipListSet<Integer> setObj = new ConcurrentSkipListSet();
        for (int i = 0; i < 50; i++) {
            fixedThreadPool.submit(() -> {
                Singleton004 instance = Singleton004.getInstance();
                setObj.add(instance.hashCode());
            });
        }
        fixedThreadPool.shutdown();
        System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");
        System.out.println("生成类数:" + setObj.size());
        for (Integer s : setObj) {
            System.out.println("hashcode:" + s);
        }
    }



输出



new Singleton004
耗时:63ms
生成类数:1
hashcode:1052225082



版本五、 同步代码块synchronized迭代,同时增加volatile,双锁DCL【可以】

 

JDK 版本:JDK1.5 起

 

是否 Lazy 初始化:

 

是否多线程安全:

 

实现难度:较复杂

 

描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。

getInstance() 的性能对应用程序很关键。

 



public class Singleton005 {

    /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
    volatile private static Singleton005 instance = null;

    /* 私有构造方法,防止被实例化 */
    private Singleton005() {
        System.out.println("new Singleton005");
    }

    /* 静态工程方法,创建实例 */
    public static Singleton005 getInstance() {
        if (instance == null) {
            synchronized (Singleton005.class) {
                if (instance == null) {
                    instance = new Singleton005();
                }
            }
        }
        return instance;
    }

    /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
    public Object readResolve() {
        return instance;
    }
}



测试



@Test
    public void getInstance() {
        long start = System.currentTimeMillis();
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(50);
//        Set<Integer> setObj = new HashSet<>(); //线程并发问题
        ConcurrentSkipListSet<Integer> setObj = new ConcurrentSkipListSet();
        for (int i = 0; i < 500; i++) {
            fixedThreadPool.submit(() -> {
                Singleton005 instance = Singleton005.getInstance();
                setObj.add(instance.hashCode());
            });
        }
        fixedThreadPool.shutdown();
        System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");
        System.out.println("生成类数:" + setObj.size());
        for (Integer s : setObj) {
            System.out.println("hashcode:" + s);
        }
    }



输出 



new Singleton005
耗时:63ms
生成类数:1
hashcode:1961726163



2.2.2、方式二、静态内置类实现单例模式【可以】

是否 Lazy 初始化:

是否多线程安全:

实现难度:一般

描述:这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。

这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟饿汉方式不同的是:饿汉方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比饿汉方式就显得很合理。



public class Singleton006 {

    /* 私有构造方法,防止被实例化 */
    private Singleton006() {
        System.out.println("new Singleton006");
    }

    /* 静态工程方法,创建实例 */
    public static Singleton006 getInstance() {
        return SingletonFactory.instance;
    }

    //内部类
    private static class SingletonFactory {
        private static Singleton006 instance = new Singleton006();
    }


    //该方法在反序列化时会被调用,该方法不是接口定义的方法,有点儿约定俗成的感觉
    protected Object readResolve() throws ObjectStreamException {
        System.out.println("调用了readResolve方法!");
        return SingletonFactory.instance;
    }
}



测试



@Test
    public void getInstance() {
        long start = System.currentTimeMillis();
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(50);
//        Set<Integer> setObj = new HashSet<>(); //线程并发问题
        ConcurrentSkipListSet<Integer> setObj = new ConcurrentSkipListSet();
        for (int i = 0; i < 500; i++) {
            fixedThreadPool.submit(() -> {
                Singleton006 instance = Singleton006.getInstance();
                setObj.add(instance.hashCode());
            });
        }
        fixedThreadPool.shutdown();
        System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");
        System.out.println("生成类数:" + setObj.size());
        for (Integer s : setObj) {
            System.out.println("hashcode:" + s);
        }
    }



输出



new Singleton006
耗时:67ms
生成类数:1
hashcode:752816814



看似完美,但是如果在构造函数中抛出异常,实例将永远得不到创建,也会出错。

2.2.3、方式三、使用static代码块实现单例【可以】

静态代码块中的代码在使用类的时候就已经执行了,所以可以应用静态代码块的这个特性的实现单例设计模式。



public class Singleton007 {

    private static Singleton007 instance = null;

    /* 私有构造方法,防止被实例化 */
    private Singleton007() {
        System.out.println("new Singleton007");
    }

    static {
        instance = new Singleton007();
    }

        /* 静态工程方法,创建实例 */
    public static Singleton007 getInstance() {
            return instance;
    }
}



测试



@Test
    public void getInstance() {
        long start = System.currentTimeMillis();
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(50);
//        Set<Integer> setObj = new HashSet<>(); //线程并发问题
        ConcurrentSkipListSet<Integer> setObj = new ConcurrentSkipListSet();
        for (int i = 0; i < 500; i++) {
            fixedThreadPool.submit(() -> {
                Singleton007 instance = Singleton007.getInstance();
                setObj.add(instance.hashCode());
            });
        }
        fixedThreadPool.shutdown();
        System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");
        System.out.println("生成类数:" + setObj.size());
        for (Integer s : setObj) {
            System.out.println("hashcode:" + s);
        }
    }



输出



new Singleton007
耗时:67ms
生成类数:1
hashcode:1046297923



看似完美,但是如果在构造函数中抛出异常,实例将永远得不到创建,也会出错。

2.2.4、方式四、使用enum代码块实现单例【可以,推荐】

JDK 版本:JDK1.5 起

是否 Lazy 初始化:

是否多线程安全:

实现难度:

描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。

这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。

枚举enum和静态代码块的特性相似,在使用枚举时,构造方法会被自动调用



public class Singleton008 {

    private enum MyEnumSingleton{
        singletonFactory;

        private Singleton008 instance;

        private MyEnumSingleton(){//枚举类的构造方法在类加载是被实例化
            instance = new Singleton008();
        }

        public Singleton008 getInstance(){
            return instance;
        }
    }


    /* 私有构造方法,防止被实例化 */
    private Singleton008() {
        System.out.println("new Singleton008");
    }
    
    public static Singleton008 getInstance(){
        return MyEnumSingleton.singletonFactory.getInstance();
    }
}



或者单纯使用



public enum Singleton012enum {
    INSTANCE;

    Singleton012enum() {
        System.out.println("ctor Singleton012enum");
    }

    public static Singleton012enum getInstance(){
        return INSTANCE;
    }
    public void whateverMethod() {
        System.out.println("ss");
    }
}



 

也是可以的

测试



@Test
    public void getInstance() {
        long start = System.currentTimeMillis();
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(50);
//        Set<Integer> setObj = new HashSet<>(); //线程并发问题
        ConcurrentSkipListSet<Integer> setObj = new ConcurrentSkipListSet();
        for (int i = 0; i < 500; i++) {
            fixedThreadPool.submit(() -> {
                Singleton008 instance = Singleton008.getInstance();
                setObj.add(instance.hashCode());
            });
        }
        fixedThreadPool.shutdown();
        System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");
        System.out.println("生成类数:" + setObj.size());
        for (Integer s : setObj) {
            System.out.println("hashcode:" + s);
        }
    }



输出



new Singleton008
耗时:72ms
生成类数:1
hashcode:346401817



2.2.5、方式五、线程单例

这种方式只能保证在一个线程内拿到单例对象



public class Singleton013thread {

    private static final ThreadLocal<Singleton013thread> treadLocalInstance =
            new ThreadLocal<Singleton013thread>() {
                @Override
                protected Singleton013thread initialValue() {
                    return new Singleton013thread();
                }
            };

    private Singleton013thread() {
        System.out.println("ctor Singleton013thread");
    }

    public static Singleton013thread getInstance() {
        return treadLocalInstance.get();
    }
}



 

测试



@Test
    public void getInstance() throws Exception {
        long start = System.currentTimeMillis();
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
//        Set<Integer> setObj = new HashSet<>(); //线程并发问题
        ConcurrentSkipListSet<Integer> setObj = new ConcurrentSkipListSet();
        for (int i = 0; i < 5; i++) {
            fixedThreadPool.submit(() -> {
                Singleton013thread instance = Singleton013thread.getInstance();
                Singleton013thread instance2 = Singleton013thread.getInstance();
                setObj.add(instance.hashCode());
                setObj.add(instance2.hashCode());
            });
        }
        fixedThreadPool.shutdown();
        System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");
        System.out.println("生成类数:" + setObj.size());
        Thread.sleep(1000);
        for (Integer s : setObj) {
            System.out.println("hashcode:" + s);
        }
    }



 

输出



耗时:57ms
生成类数:0
ctor Singleton013thread
ctor Singleton013thread
ctor Singleton013thread
ctor Singleton013thread
ctor Singleton013thread
hashcode:346401817
hashcode:421396084
hashcode:1046297923
hashcode:1425807869
hashcode:2049597926



 

三、扩展

3.1、spring实现的单例

在Spring中,bean可以被定义为两种模式:prototype(多例)和singleton(单例)

singleton(单例):只有一个共享的实例存在,所有对这个bean的请求都会返回这个唯一的实例。

prototype(多例):对这个bean的每次请求都会创建一个新的bean实例,类似于new。

Spring bean 默认是单例模式。

pom依赖




spring 手动创建消息队列 spring创建单例_spring 手动创建消息队列

spring 手动创建消息队列 spring创建单例_runtime_02

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>


View Code


3.1.1、单例示例

基础类



public class Singleton009spring {
    public Singleton009spring() {
        System.out.println("ctor Singleton009spring");
    }

    public void init() {
        System.out.println("init Singleton009spring");
    }
}



xml配置



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="singleton009spring" class="com.github.bjlhx15.patterns.base.create.singleton.Singleton009spring"
          init-method="init" scope="singleton"></bean>
</beans>



测试 



@Test
    public void test_singleton() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Singleton009spring hi1 = (Singleton009spring) context.getBean("singleton009spring");
        Singleton009spring hi2 = (Singleton009spring) context.getBean("singleton009spring");
        System.out.println(hi1);
        System.out.println(hi2);
    }



输出



ctor Singleton009spring
init Singleton009spring
com.github.bjlhx15.patterns.base.create.singleton.Singleton009spring@631330c
com.github.bjlhx15.patterns.base.create.singleton.Singleton009spring@631330c



单例,二个变量指向一个对象。

3.1.2、多例模式

java bean不变

xml配置增加



<bean id="singleton009spring2" class="com.github.bjlhx15.patterns.base.create.singleton.Singleton009spring"
          init-method="init" scope="prototype"></bean>



测试



@Test
    public void test_prototype() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Singleton009spring hi1 = (Singleton009spring) context.getBean("singleton009spring2");
        Singleton009spring hi2 = (Singleton009spring) context.getBean("singleton009spring2");
        System.out.println(hi1);
        System.out.println(hi2);
    }



输出



ctor Singleton009spring
init Singleton009spring
ctor Singleton009spring
init Singleton009spring
com.github.bjlhx15.patterns.base.create.singleton.Singleton009spring@631330c
com.github.bjlhx15.patterns.base.create.singleton.Singleton009spring@42f93a98



结论:每次访问bean,均创建一个新实例。 

3.1.3、单例注册表

上述的饿汉式单例、懒汉式单例均比较遗憾不能被继承

克服前两种单例类不能被继承的缺点,我们可以使用另外一种特殊化的单例模式,它被称为单例注册表。



public class Singleton010Reg {
    static private HashMap registry = new HashMap();

    //静态块,在类被加载时自动执行
    static {
        Singleton010Reg rs = new Singleton010Reg();
        registry.put(rs.getClass().getName(), rs);
    }

    //受保护的默认构造函数,如果为继承关系,则可以调用,克服了单例类不能为继承的缺点
    protected Singleton010Reg() {
        System.out.println("cotr Singleton010Reg");
    }

    //静态工厂方法,返回此类的唯一实例
    public static Singleton010Reg getInstance(String name) {
        if (name == null||name=="") {
            name = "com.github.bjlhx15.patterns.base.create.singleton.Singleton010Reg";
        }
        if (registry.get(name) == null) {
            try {
                registry.put(name, Class.forName(name).newInstance());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return (Singleton010Reg) registry.get(name);
    }
}



测试



@Test
    public void getInstance() {
        long start = System.currentTimeMillis();
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(50);
//        Set<Integer> setObj = new HashSet<>(); //线程并发问题
        ConcurrentSkipListSet<Integer> setObj = new ConcurrentSkipListSet();
        for (int i = 0; i < 500; i++) {
            fixedThreadPool.submit(() -> {
                Singleton010Reg instance = Singleton010Reg.getInstance("");
                setObj.add(instance.hashCode());
            });
        }
        fixedThreadPool.shutdown();
        System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");
        System.out.println("生成类数:" + setObj.size());
        for (Integer s : setObj) {
            System.out.println("hashcode:" + s);
        }
    }



输出



cotr Singleton010Reg
耗时:75ms
生成类数:1
hashcode:1588170421



3.1.4、Spring单例

spring单例在注入时候使用scope控制,默认的@Controller、@Service、@Repository、@Component是单例,修改@Scope。如何实现的..



Singleton009spring hi1 = (Singleton009spring) context.getBean("singleton009spring");



3.1.4.1、bean创建过程 

从上文看到获取bean代码,可以找到BeanFactory→AbstractBeanFactory

  

spring 手动创建消息队列 spring创建单例_System_11

AbstractBeanFactory的作用  



  • 是抽象BeanFactory的基类,同时实现了ConfigurableBeanFactory的SPI,提供了所有的功能
  • 也可以从我们定义的资源中resource中来获取bean的定义.
  • 也提供了单例bean的缓存通过他的爷爷如图中的DefaultSingletonBeanRegistry,同时提供了单例和多例和别名的定义等操作.



1、getBean方法

提供了四个重在重载,如下




spring 手动创建消息队列 spring创建单例_spring 手动创建消息队列

spring 手动创建消息队列 spring创建单例_runtime_02

//通过name获取Bean
@Override
public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}
//通过name和类型获取Bean
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
    return doGetBean(name, requiredType, null, false);
}
//通过name和对象参数获取Bean
@Override
public Object getBean(String name, Object... args) throws BeansException {
    return doGetBean(name, null, args, false);
}
//通过name、类型和参数获取Bean
public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
    return doGetBean(name, requiredType, args, false);
}


View Code


从这四个重载方法的方法体中可以看出,他们都是通过doGetBean来实现的。所以doGetBean其实才是真正获取Bean的地方,也是触发依赖注入发生的地方。

在spring里面都是一般这样做的,获取某个东西,实际上封装了一层内部调用如上述代码的doGetBean()方法,对外几个getXXX重载,内部使用doGetXXX实现。

2、doGetBean

方法定义



@SuppressWarnings("unchecked")
    protected <T> T doGetBean(
    final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
    throws BeansException {



  • name 要检索的 bean 的名称
  • requiredType 要检索的bean所需的类型
  • args 使用显式参数创建bean 实例 时使用的参数(仅在创建新实例时应用,而不是在检索现有实例时应用)
  • typeCheckOnly 是否为类型检查而获得实例,而不是实际使用

实现说明



protected <T> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
        //返回bean名称,剥离工厂引用前缀,并将别名解析为规范名称,该方法去掉别名之后的bean的name,
        final String beanName = this.transformedBeanName(name);

        //从上面的去掉别名的beanName拿到后,从缓存中获取bean,处理已经被创建的单例模式的bean,调用了DefaultSingletonBeanRegistry的getSingleton方法
        //对于此类bean的请求不需要重复的创建(singleton)
        Object sharedInstance = this.getSingleton(beanName);

        //声明当前需要返回的bean对象
        Object bean;

        // 如果当前获取到的sharedInstance不为null并且参数为空,则进行FactoryBean的相关处理,并获取FactoryBean的处理结果。
        if (sharedInstance != null && args == null) {
            if (this.logger.isDebugEnabled()) {
                if (this.isSingletonCurrentlyInCreation(beanName)) {
                    this.logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
                } else {
                    this.logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            //完成FactoryBean的相关处理,并用来获取FactoryBean的处理结果
            bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition) null);
        } else {//如果当前获取到的sharedInstance为null
            
            //在当前线程中,返回指定的prototype bean是否正在创建。如果当前的bean已经被创建,获取会失败,可能有别的bean在引用该bean
            if (this.isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }

            // 下面这段 是对Ioc容器中的 BeanDefinition 是否存在进行检测,先是检测当前BeanFactory中是否能够获取到,
            // 如果取不到则继续到双亲容器中进行尝试获取,如果双亲还是取不到,则继续向上一级父容器中尝试获取。
            
            // 检查该工厂是否存在bean定义。
            BeanFactory parentBeanFactory = this.getParentBeanFactory();
            if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {
                // 如果没有,则继续检查父类
                String nameToLookup = this.originalBeanName(name);
                if (args != null) {
                     用明确的参数代表父项。
                    return parentBeanFactory.getBean(nameToLookup, args);
                }
                // 如果没有args - >委托给标准的getBean方法。
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
            // 将指定的bean标记为已经创建(或即将创建);这里允许bean工厂优化其缓存以重复创建指定的bean。
            if (!typeCheckOnly) {
                this.markBeanAsCreated(beanName);
            }

            try {
                // 先根据beanName来获取BeanDefinition,然后获取当前bean的所有依赖bean,这里是通过递归调用getBean来完成,直到没有任何依赖的bean为止。
                final RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
                检查给定的合并bean定义,可能抛出验证异常。
                this.checkMergedBeanDefinition(mbd, beanName, args);
                // 保证当前bean依赖的bean的初始化。
                String[] dependsOn = mbd.getDependsOn();
                String[] var11;
                if (dependsOn != null) {
                    var11 = dependsOn;
                    int var12 = dependsOn.length;

                    for (int var13 = 0; var13 < var12; ++var13) {
                        String dep = var11[var13];
                        if (this.isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                        }

                        this.registerDependentBean(dep, beanName);

                        try {
                            //递归处理依赖bean
                            this.getBean(dep);
                        } catch (NoSuchBeanDefinitionException var24) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", var24);
                        }
                    }
                }
                //下面是创建singleton bean
                if (mbd.isSingleton()) {
                    // 下面这段就是创建一个bean实例;这里通过调用getSingleton方法来创建一个单例bean实例;
                    // 从 代码 中可以看到,getSingleton的调用是通过getObject这个回调函数来间接调用createBean完成的。
                    sharedInstance = this.getSingleton(beanName, new ObjectFactory<Object>() {
                        //回调函数getObject
                        public Object getObject() throws BeansException {
                            try {
                                创建bean
                                return AbstractBeanFactory.this.createBean(beanName, mbd, args);
                            } catch (BeansException var2) {
                                //发生异常则销毁
                                AbstractBeanFactory.this.destroySingleton(beanName);
                                throw var2;
                            }
                        }
                    });
                    //获取给定bean实例的对象,无论是bean实例本身,还是FactoryBean创建的对象。
                    bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                } 
                //下面是创建 多例模式 prototype bean
                else if (mbd.isPrototype()) {
                    var11 = null;

                    Object prototypeInstance;
                    try {
                        this.beforePrototypeCreation(beanName);
                        //创建prototype bean
                        prototypeInstance = this.createBean(beanName, mbd, args);
                    } finally {
                        this.afterPrototypeCreation(beanName);
                    }

                    bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                } else {
                    //获取bean的作用域
                    String scopeName = mbd.getScope();
                    Scope scope = (Scope) this.scopes.get(scopeName);
                    if (scope == null) {
                        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                    }

                    try {
                        Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                            public Object getObject() throws BeansException {
                                AbstractBeanFactory.this.beforePrototypeCreation(beanName);

                                Object var1;
                                try {
                                    var1 = AbstractBeanFactory.this.createBean(beanName, mbd, args);
                                } finally {
                                    AbstractBeanFactory.this.afterPrototypeCreation(beanName);
                                }

                                return var1;
                            }
                        });
                        bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                    } catch (IllegalStateException var23) {
                        throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", var23);
                    }
                }
            } catch (BeansException var26) {
                this.cleanupAfterBeanCreationFailure(beanName);
                throw var26;
            }
        }

        //最后是对创建的bean进行类型检查,没有问题就返回已经创建好的bean;此时这个bean是包含依赖关系的bean
        //检查类型与实际的bean的是否匹配
        if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
            try {
                return this.getTypeConverter().convertIfNecessary(bean, requiredType);
            } catch (TypeMismatchException var25) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", var25);
                }

                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
        } else {
            return bean;
        }
    }



上面代码是spring如何获取bean的过程,实际最核心的是createBean() 方法,该方法真正的创建bean的过程

3、createBean




spring 手动创建消息队列 spring创建单例_spring 手动创建消息队列

spring 手动创建消息队列 spring创建单例_runtime_02

/**
     * 该方法是AbstractAutowireCapableBeanFactory中的实现方法
     *
     */
    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {

        if (logger.isTraceEnabled()) {
            logger.trace("Creating instance of bean '" + beanName + "'");
        }
        RootBeanDefinition mbdToUse = mbd;

        // Make sure bean class is actually resolved at this point, and
        // clone the bean definition in case of a dynamically resolved Class
        // which cannot be stored in the shared merged bean definition.
        //解析bean的class
        //1.这里当解析的resolvedClass和mbd.hasBeanClass()为falsembd.getBeanClassName()同时成立时
        //2.这里需要我们new一个合并的beanDefinition类,同时设置class类型
        Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
        if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
            mbdToUse = new RootBeanDefinition(mbd);
            mbdToUse.setBeanClass(resolvedClass);
        }

        //覆盖了RootBeanDefinition的prepareMethodOverrides方法
        try {
            mbdToUse.prepareMethodOverrides();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
                    beanName, "Validation of method overrides failed", ex);
        }

        try {
            //获取一个解析之后的代理bean的实例,而不是真正的bean实例
            Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
            if (bean != null) {
                return bean;
            }
        }
        catch (Throwable ex) {
            throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                    "BeanPostProcessor before instantiation of bean failed", ex);
        }

        try {
            //这里才是真正的执行创建bean的方法
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isTraceEnabled()) {
                logger.trace("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
        }
        catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
            // A previously detected exception with proper bean creation context already,
            // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
        }
    }


View Code


按照spring玩法,会有一个doCreateBean真正实现,先看createBean方法

4、doCreateBean



/**
     * 该方法是真正的创建bean的方法
     * @param beanName 要创建bean的名称
     * @param mbd 定义bean的beanDefinition文件
     * @param args 创建bean时,构造方法所需的参数或者是调用bean工厂时来创建bean所需的参数
     * @return 完成创建之后的bean的实例
     * @throws BeanCreationException
     */
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {

        //实例化bean的过程
        BeanWrapper instanceWrapper = null;
        //如果是单例,从factoryBeanInstanceCache中移除相应的bean实例
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        //创建实例
        if (instanceWrapper == null) {
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        //获取封装实例
        final Object bean = instanceWrapper.getWrappedInstance();
        //获取对应实例的class类型
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            //将获取到的bean类型赋值给mbd的resolvedTargetType
            //resolvedTargetType用来保存真实类型bean的类型
            mbd.resolvedTargetType = beanType;
        }

        // Allow post-processors to modify the merged bean definition.
        //允许后置处理修改mbd
        synchronized (mbd.postProcessingLock) {
            //postProcessed默认为false,故不能修改,这里取反表示可以修改
            if (!mbd.postProcessed) {
                try {
                    //对bean进行后置处理
                    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                            "Post-processing of merged bean definition failed", ex);
                }
                mbd.postProcessed = true;
            }
        }

        //处理循环引用和bean的生命周期
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isTraceEnabled()) {
                logger.trace("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
        //添加单例 至 单例工厂
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

        //初始化bean实例
        Object exposedObject = bean;
        try {
            //给bean的实例填充属性
            populateBean(beanName, mbd, instanceWrapper);
            //初始化bean的实例
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }
        //引用问题处理,这里先暴露早期单例引用的bean
        if (earlySingletonExposure) {
            //从缓存注册中获取,这里不允许早期引用的创建
            Object earlySingletonReference = getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                }
                else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                    String[] dependentBeans = getDependentBeans(beanName);
                    Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                    for (String dependentBean : dependentBeans) {
                        if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                            actualDependentBeans.add(dependentBean);
                        }
                    }
                    if (!actualDependentBeans.isEmpty()) {
                        throw new BeanCurrentlyInCreationException(beanName,
                                "Bean with name '" + beanName + "' has been injected into other beans [" +
                                        StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                                        "] in its raw version as part of a circular reference, but has eventually been " +
                                        "wrapped. This means that said other beans do not use the final version of the " +
                                        "bean. This is often the result of over-eager type matching - consider using " +
                                        "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
                    }
                }
            }
        }

        //注册bean
        try {
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
        }

        return exposedObject;
    }



查看注册bean的registerDisposableBeanIfNecessary



/**
     * AbstractBeanFactory实现 注册bean
     * @param beanName
     * @param bean
     * @param mbd
     */
    protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
        AccessControlContext acc = System.getSecurityManager() != null ? this.getAccessControlContext() : null;
        if (!mbd.isPrototype() && this.requiresDestruction(bean, mbd)) {
            if (mbd.isSingleton()) {
                // 单例注册
                this.registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, mbd, this.getBeanPostProcessors(), acc));
            } else {
                Scope scope = (Scope)this.scopes.get(mbd.getScope());
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
                }

                scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(bean, beanName, mbd, this.getBeanPostProcessors(), acc));
            }
        }
    }



查看registerDisposableBean



public void registerDisposableBean(String beanName, DisposableBean bean) {
        synchronized(this.disposableBeans) {
            this.disposableBeans.put(beanName, bean);
        }
    }



其中:disposableBeans是



private final Map<String, Object> disposableBeans = new LinkedHashMap();



3.1.4.2、单例获取



Object sharedInstance = this.getSingleton(beanName);



核心方法



//DefaultSingletonBeanRegistry
    public Object getSingleton(String beanName) {
        return this.getSingleton(beanName, true);
    }

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        //去map读取 private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
            synchronized(this.singletonObjects) {
                //去map读取  private final Map<String, Object> earlySingletonObjects = new HashMap(16);
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    // 去工厂 获取工厂
                    ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        // 工厂获取对象 放到 earlySingletonObjects 从工厂移除
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }

        return singletonObject != NULL_OBJECT ? singletonObject : null;
    }



1、singletonObjects如何被添加注册单例MAP上的

可以在上述创建bean代码中看到,如果是单例



if (mbd.isSingleton()) {
                    sharedInstance = this.getSingleton(beanName, new ObjectFactory<Object>() {
                        public Object getObject() throws BeansException {
                            try {
                                return AbstractBeanFactory.this.createBean(beanName, mbd, args);
                            } catch (BeansException var2) {
                                AbstractBeanFactory.this.destroySingleton(beanName);
                                throw var2;
                            }
                        }
                    });
                    bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }



2、这里的getSingleton如下



//DefaultSingletonBeanRegistry
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "'beanName' must not be null");
        synchronized(this.singletonObjects) {
            // 去注册MAP获取
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                //…………
                this.beforeSingletonCreation(beanName);
                boolean newSingleton = false;
                boolean recordSuppressedExceptions = this.suppressedExceptions == null;
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = new LinkedHashSet();
                }

                try {
                    //工厂获取
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                } catch (IllegalStateException var16) {
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        throw var16;
                    }
                } catch (BeanCreationException var17) {
                    BeanCreationException ex = var17;
                    if (recordSuppressedExceptions) {
                        Iterator var8 = this.suppressedExceptions.iterator();

                        while(var8.hasNext()) {
                            Exception suppressedException = (Exception)var8.next();
                            ex.addRelatedCause(suppressedException);
                        }
                    }

                    throw ex;
                } finally {
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = null;
                    }

                    this.afterSingletonCreation(beanName);
                }
                //添加到单例注册MAP
                if (newSingleton) {
                    this.addSingleton(beanName, singletonObject);
                }
            }

            return singletonObject != NULL_OBJECT ? singletonObject : null;
        }
    }



 3、对于添加addSingleton,以及singletonObjects,比较简单如下



protected void addSingleton(String beanName, Object singletonObject) {
        synchronized(this.singletonObjects) {
            this.singletonObjects.put(beanName, singletonObject != null ? singletonObject : NULL_OBJECT);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }



4、 工厂获取

在第1段代码,第2段代码,均有



singletonObject = singletonFactory.getObject();



第1段调用属性



ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);



private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);



第2段是参数 传入

  但是均有一个singletonFactories

通过代码可以看到 DefaultSingletonBeanRegistry 添加了单例工厂



protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized(this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }

        }
    }



可以参看上述 doCreateBean 代码中的



//处理循环引用和bean的生命周期
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isTraceEnabled()) {
                logger.trace("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
        //添加单例 至 单例工厂
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }



3.1.4.3、Spring单例过程

1、getBean:客户调用:Singleton009spring hi1 = (Singleton009spring) context.getBean("singleton009spring");

  内部调用的是:org.springframework.beans.factory.BeanFactory#getBean(java.lang.String)

  具体实现是:org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)

2、doGetBean:上述getBean实际调用doGetBean

2.1、执行1、getSingleton:调用:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String)

  →:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)

    内部会查询:singletonObjects = new ConcurrentHashMap(256);

        earlySingletonObjects = new HashMap(16);

      上述均没有会从工厂获取:(ObjectFactory)this.singletonFactories.get(beanName);

      工厂没有的话放弃单例查询,

2.2、执行二、如果是单例会调用 :sharedInstance = this.getSingleton

    :org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)

    参数:第2个入参:AbstractBeanFactory.this.createBean(beanName, mbd, args);

      执行:org.springframework.beans.factory.support.AbstractBeanFactory#createBean

      实际调用:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])

        内部调用:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

          内部调用:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory,此时DefaultSingletonBeanRegistry含有了singletonFactories = new HashMap(16);

    getSingleton内部执行:  

      singletonObject = singletonFactory.getObject();

      添加:this.addSingleton(beanName, singletonObject);,添加至 singletonObjects = new ConcurrentHashMap(256);

  第一次执行完毕会返回此bean

2.3、上述2.1中,内部查询

  是singletonObjects,会去取出单例

3.1.4.4、验证spring webmvc中@Controller,@Service等的单例

POM




spring 手动创建消息队列 spring创建单例_spring 手动创建消息队列

spring 手动创建消息队列 spring创建单例_runtime_02

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.17.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>4.3.17.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.17.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.3.17.RELEASE</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.3.17.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.17.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>


View Code


代码Service




spring 手动创建消息队列 spring创建单例_spring 手动创建消息队列

spring 手动创建消息队列 spring创建单例_runtime_02

@Service
public class Singleton012springService {
    public Singleton012springService() {
        System.out.println("ctor Singleton012springService");
    }

    public void init() {
        System.out.println("init Singleton012springService");
    }

    private String name;

    public String getName() {

        System.out.println("Service hashcode:"+this.hashCode());
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String methodSetName(String name){
        setName(name);
        return getName();
    }
}


View Code


代码Controller




spring 手动创建消息队列 spring创建单例_spring 手动创建消息队列

spring 手动创建消息队列 spring创建单例_runtime_02

@Controller
public class Singleton012springController {
    @Autowired
    private Singleton012springService service;

    private String add;

    public String getAdd() {
        return add;
    }

    public void setAdd(String add) {
        this.add = add;
    }

    @RequestMapping("test")
    public String test(String id){
        if(id.equals("1")){
            setAdd("京东总部");
            service.methodSetName("李宏旭");
        }
        System.out.println("Controller 属性:"+getAdd());
        System.out.println("Controller hashcode:"+this.hashCode());


        String name = service.getName();
        System.out.println("Service 属性:"+name);

        return "OK";
    }
}


View Code


xml



<context:component-scan base-package="com.github.bjlhx15.patterns.base.create.singleton.service"></context:component-scan>



单元测试



@RunWith(SpringJUnit4ClassRunner.class)  //使用junit4进行测试
@ContextConfiguration({"classpath:applicationContext-012.xml"}) //加载配置文件
//加上 @WebAppConfiguration
@WebAppConfiguration
public class Singleton012springControllerTest {

    @Autowired
    private WebApplicationContext webApplicationContext;

    private MockMvc mockMvc;

    //方法执行前初始化数据
    @Before
    public void setUp() throws Exception {
        //No qualifying bean of type 'org.springframework.web.context.WebApplicationContext' available
        //加上 @WebAppConfiguration
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

    @Test
    public void testControlersingleton() throws Exception {
        MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders.get("/test");
        mockHttpServletRequestBuilder.param("id", "1"); //要传入的参数
        ResultActions resultActions = mockMvc.perform(mockHttpServletRequestBuilder);
        resultActions.andExpect(status().isOk());

        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(50);
        for (int i = 0; i < 50; i++) {
            fixedThreadPool.submit(() -> {
                MockHttpServletRequestBuilder mockHttpServletRequestBuilder1 = MockMvcRequestBuilders.get("/test");
                mockHttpServletRequestBuilder1.param("id", "2"); //要传入的参数
                try {
                    ResultActions resultActions1 = mockMvc.perform(mockHttpServletRequestBuilder1);
                    resultActions1.andExpect(status().isOk());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        fixedThreadPool.shutdown();
    }



输出



Service hashcode:1464555023
Controller 属性:京东总部
Controller hashcode:195381554
Service hashcode:1464555023
Service 属性:李宏旭
Controller 属性:京东总部
Controller hashcode:195381554
Service hashcode:1464555023
Service 属性:李宏旭
//……



从Controller和Service的Hashcode可以看出,是单例模式

3.1.4.5、SpringMVC默认单例模式如何保证性能、以及多线程数据唯一性的

  spring对象是单例的,但类里面方法对每个线程来说都是独立运行的,不存在多线程问题,只有成员变量有多线程问题,所以方法里面如果有用到成员变量就要考虑用安全的数据结构。对于成员变量的操作,可以使用ThreadLocal来保证线程安全

  方法都是独立的,每个用户在访问的时候单独开辟了空间,而成员变量却是共有的,所有用户都是调用的同一个。每个方法中对局部变量的操作都是在线程自己独立的内存区域内完成的,所以是线程安全的。

3.2、JDK源码中的单例-Runtime

  通过查看java.lang.Runtime静态成员变量currentRuntime、getRunTime()方法、私有构造器,可知是一个单例模式的饿汉式。



public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    public static Runtime getRuntime() {
        return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}
    //……    
}



3.3、AbstractFactoryBean——懒汉式

 在org.springframework.beans.factory.config.AbstractFactoryBeangetObject() 方法中,查看调用getEarlySingletonInstance()



public final T getObject() throws Exception {
        if (this.isSingleton()) {
            return this.initialized ? this.singletonInstance : this.getEarlySingletonInstance();
        } else {
            return this.createInstance();
        }
    }



使用了懒汉式,初始化单例对象



private T getEarlySingletonInstance() throws Exception {
        Class<?>[] ifcs = this.getEarlySingletonInterfaces();
        if (ifcs == null) {
            throw new FactoryBeanNotInitializedException(this.getClass().getName() + " does not support circular references");
        } else {
            if (this.earlySingletonInstance == null) {
                this.earlySingletonInstance = Proxy.newProxyInstance(this.beanClassLoader, ifcs, new AbstractFactoryBean.EarlySingletonInvocationHandler());
            }

            return this.earlySingletonInstance;
        }
    }