单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
一、单例模式的定义
单例模式指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点;所以想到的第一个创建方式就是隐藏掉它所有的构造方法,让用户不是通过构造方法去构造,而是通过他自身的类去构造一个实例
二、单例模式的应用场景
代码接近于生活,很有意思。比如一个网站的登录,点击登录后弹出一个登录弹框,即使再次点击,也不会再出现一个相同的弹框。又或者一个音乐播放程序,如果用户打开了一个音乐,又想打开一个音乐,那么之前的播放界面就会自动关闭,切换到当前的播放界面。这些都是单例模式的应用场景。
要实现一个单例模式,一个经典的方式是创建一个类,类中又一个方法能创建该类的实例对象,还有一个标记,记录是否已经创了过了实例对象。如果对象已经存在,就返回第一次实例化对象的引用
三、饿汉式单例
/** *优点:没有加任何的锁、执行效率比较高,在用户体验上来说,比懒汉式更好。 *缺点:类加载的时候就初始化,不管用与不用都占着空间,浪费了内存。 */ public class TheHungry { private static final TheHungry theHungry=new TheHungry(); private TheHungry(){} private static TheHungry getInstance(){ return theHungry; } }
四、懒汉式单例
/** * 优点:被外部类调用的时候内部类才会加载 * 缺点:线程不安全 */ public class Idler { private static Idler idler=null; private Idler(){} private static Idler getInstance(){ if (idler==null){ return idler= new Idler(); } return idler; } }
public class ExectorThread implements Runnable { @Override public void run() { Idler idler= Idler.getInstance(); System.out.println(Thread.currentThread().getName()+":"+idler); } }
public class Test { 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("结束"); } }
从下图看可以发现创建了两个对象;下面分析下为什么出现这种情况:
那么怎么解决线程安全问题呢,第一个想到的加锁
public class Idler { private static Idler idler=null; private Idler(){} public synchronized static Idler getInstance(){ if (idler==null){ return idler= new Idler(); } return idler; } }
public class Idler { private static Idler idler=null; private Idler(){} public static Idler getInstance(){ if (idler==null){ synchronized (Idler.class){ if (idler==null){ return idler= new Idler(); //1 } } } return idler; } }
上面代码看起来完美,但看过我写的多线程文章的朋友可能看出来了,上面代码带来了另一个问题,那就是指令重排序问题;问题的根源在于 1创建对象
的流程可能会被重排序。在聊指令重排前先聊一下创建对象的一般流程:
创建对象的流程可以简化为三步:分配内存空间、初始化对象、设置引用指向分配的内存地址。
memory = allocate(); // 1、分配对象的内存空间
initObject(memory); // 2、初始化对象
instance = memory; // 3、设置instance指向分配的内存地址
其中,2 和 3 之间可能被重排序。为什么看我的多线程文章;重排序之后的流程
memory = allocate(); // 1、分配对象的内存空间
instance = memory; // 2、设置instance指向分配的内存地址
initObject(memory); // 3、初始化对象
那么,在发生重排序的情况下,A
、B
两个线程执行这段双重检查锁定的代码时的执行顺序可能是这样的:
按照以上的顺序,B
线程将会在instance
对象还未初始化完成之前就访问它,导致NPE
。解决这问题方法也很简单加个volatile关健字就好
public class Idler { private volatile static Idler idler=null; // 声明为volatile类型 private Idler(){} public static Idler getInstance(){ if (idler==null){ synchronized (Idler.class){ if (idler==null){ return idler= new Idler(); } } } return idler; } }
volatile
通过禁止重排序(初始化对象和设置instance
指向分配的内存地址)来保证线程安全的延迟初始化。但是,用到 synchronized 关键字,总归是要上锁,对程序性能还是存在一定影响的。可以从类初始化角度来考虑,看下面的代码,采用静态内部类的方式
//这种形式兼顾饿汉式的内存浪费,也兼顾 synchronized 性能问题 // 完美地屏蔽了这两个缺点 public class Single { //默认使用 Single 的时候,会先初始化内部类 //如果没使用的话,内部类是不加载的 private Single(){} //每一个关键字都不是多余的 // static 是为了使单例的空间共享 // 保证这个方法不会被重写,重载 public static final Single getInstance(){ //在返回结果以前,一定会先加载内部类 return getInstance.INSTANCE; } //默认不加载 private static class getInstance{ private static final Single INSTANCE = new Single(); } }
五、反射破坏单例
public class Test { 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("结束"); //反射破坏单例 Class<?> clazz=Single.class; //通过反射拿到私有的构造方法= Constructor c= null; try { c = clazz.getDeclaredConstructor(null); //强吻 c.setAccessible(true); //暴力初始化 Object object=c.newInstance(); //调用了两次构造方法,相当于 new 了两次 Object object1=c.newInstance(); System.out.println(object); System.out.println(object1); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }
看结果发现单例被破坏了
竟然问题是出现在构造方法上,那在其构造方法中做一些限制,一旦出现多次重复创建,则直接抛出异常。来看优化后的代码:
//这种形式兼顾饿汉式的内存浪费,也兼顾 synchronized 性能问题 // 完美地屏蔽了这两个缺点 public class Single { //默认使用 Single 的时候,会先初始化内部类 //如果没使用的话,内部类是不加载的 private Single(){ //在构造方法中加入判断 if(getInstance.INSTANCE != null) { throw new RuntimeException("已经有对象了"); } } //每一个关键字都不是多余的 // static 是为了使单例的空间共享 // 保证这个方法不会被重写,重载 public static final Single getInstance(){ //在返回结果以前,一定会先加载内部类 return getInstance.INSTANCE; } //默认不加载 private static class getInstance{ private static final Single INSTANCE = new Single(); } }
到这里单例才算完成
六、序列化破坏单例
//反序列化时导致单例破坏 public class Serialization implements Serializable { //序列化就是说把内存中的状态通过转换成字节码的形式 // 从而转换一个 IO 流,写入到其他地方(可以是磁盘、网络 IO) // 内存中状态给永久保存下来了 // 反序列化 // 将已经持久化的字节码内容,转换为 IO 流 / // /通过 IO 流的读取,进而将读取的内容转换为 Java 对象 // 在转换过程中会重新创建对象 new public final static Serialization INSTANCE = new Serialization(); private Serialization(){} public static Serialization getInstance(){ return INSTANCE; } }
public class SerializationTest { public static void main(String[] args) { Serialization serialization=Serialization.getInstance(); Serialization serialization1=null; FileOutputStream fos = null; try { fos = new FileOutputStream("ghy"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(serialization); oos.flush(); oos.close(); FileInputStream fis = new FileInputStream("ghy"); ObjectInputStream ois = new ObjectInputStream(fis); serialization1 = (Serialization)ois.readObject(); ois.close(); System.out.println(serialization); System.out.println(serialization1); } catch (Exception e) { e.printStackTrace(); } } }
运行结果显示单例被破坏了
可以看出,反序列化后的对象和手动创建的对象是不一致的,实例化了两次,违背了单例的设计初衷。其实在这种情况下实现单例很简单,只需要增加 readResolve()方法即可。
//反序列化时导致单例破坏 public class Serialization implements Serializable { //序列化就是说把内存中的状态通过转换成字节码的形式 // 从而转换一个 IO 流,写入到其他地方(可以是磁盘、网络 IO) // 内存中状态给永久保存下来了 // 反序列化 // 将已经持久化的字节码内容,转换为 IO 流 / // /通过 IO 流的读取,进而将读取的内容转换为 Java 对象 // 在转换过程中会重新创建对象 new public final static Serialization INSTANCE = new Serialization(); private Serialization(){} public static Serialization getInstance(){ return INSTANCE; } private Object readResolve(){return INSTANCE;} }
测试程序一样,我就不截图了,走到这里一定在想一个readResolve()方法为什么就能保证单例,如果想搞清楚这个就要看看JDK的源码实现,进入ObjectInputStream类的readObject()方法
public final Object readObject() throws IOException, ClassNotFoundException { if (enableOverride) { return readObjectOverride(); } // if nested read, passHandle contains handle of enclosing object int outerHandle = passHandle; try { Object obj = readObject0(false); handles.markDependency(outerHandle, passHandle); ClassNotFoundException ex = handles.lookupException(passHandle); if (ex != null) { throw ex; } if (depth == 0) { vlist.doCallbacks(); } return obj; } finally { passHandle = outerHandle; if (closed && depth == 0) { clear(); } } }
private Object readObject0(boolean unshared) throws IOException { boolean oldMode = bin.getBlockDataMode(); if (oldMode) { int remain = bin.currentBlockRemaining(); if (remain > 0) { throw new OptionalDataException(remain); } else if (defaultDataEnd) { /* * Fix for 4360508: stream is currently at the end of a field * value block written via default serialization; since there * is no terminating TC_ENDBLOCKDATA tag, simulate * end-of-custom-data behavior explicitly. */ throw new OptionalDataException(true); } bin.setBlockDataMode(false); } byte tc; while ((tc = bin.peekByte()) == TC_RESET) { bin.readByte(); handleReset(); } depth++; totalObjectRefs++; try { switch (tc) { case TC_NULL: return readNull(); case TC_REFERENCE: return readHandle(unshared); case TC_CLASS: return readClass(unshared); case TC_CLASSDESC: case TC_PROXYCLASSDESC: return readClassDesc(unshared); case TC_STRING: case TC_LONGSTRING: return checkResolve(readString(unshared)); case TC_ARRAY: return checkResolve(readArray(unshared)); case TC_ENUM: return checkResolve(readEnum(unshared)); case TC_OBJECT: return checkResolve(readOrdinaryObject(unshared)); case TC_EXCEPTION: IOException ex = readFatalException(); throw new WriteAbortedException("writing aborted", ex); case TC_BLOCKDATA: case TC_BLOCKDATALONG: if (oldMode) { bin.setBlockDataMode(true); bin.peek(); // force header read throw new OptionalDataException( bin.currentBlockRemaining()); } else { throw new StreamCorruptedException( "unexpected block data"); } case TC_ENDBLOCKDATA: if (oldMode) { throw new OptionalDataException(true); } else { throw new StreamCorruptedException( "unexpected end of block data"); } default: throw new StreamCorruptedException( String.format("invalid type code: %02X", tc)); } } finally { depth--; bin.setBlockDataMode(oldMode); } }
private Object readOrdinaryObject(boolean unshared) throws IOException { if (bin.readByte() != TC_OBJECT) { throw new InternalError(); } ObjectStreamClass desc = readClassDesc(false); desc.checkDeserialize(); Class<?> cl = desc.forClass(); if (cl == String.class || cl == Class.class || cl == ObjectStreamClass.class) { throw new InvalidClassException("invalid class descriptor"); } Object obj; try { obj = desc.isInstantiable() ? desc.newInstance() : null; } catch (Exception ex) { throw (IOException) new InvalidClassException( desc.forClass().getName(), "unable to create instance").initCause(ex); } passHandle = handles.assign(unshared ? unsharedMarker : obj); ClassNotFoundException resolveEx = desc.getResolveException(); if (resolveEx != null) { handles.markException(passHandle, resolveEx); } if (desc.isExternalizable()) { readExternalData((Externalizable) obj, desc); } else { readSerialData(obj, desc); } handles.finish(passHandle); if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) { Object rep = desc.invokeReadResolve(obj); if (unshared && rep.getClass().isArray()) { rep = cloneArray(rep); } if (rep != obj) { // Filter the replacement object if (rep != null) { if (rep.getClass().isArray()) { filterCheck(rep.getClass(), Array.getLength(rep)); } else { filterCheck(rep.getClass(), -1); } } handles.setObject(passHandle, obj = rep); } } return obj; }
boolean isInstantiable() { requireInitialized(); return (cons != null); }
private Object readOrdinaryObject(boolean unshared) throws IOException { if (bin.readByte() != TC_OBJECT) { throw new InternalError(); } ObjectStreamClass desc = readClassDesc(false); desc.checkDeserialize(); Class<?> cl = desc.forClass(); if (cl == String.class || cl == Class.class || cl == ObjectStreamClass.class) { throw new InvalidClassException("invalid class descriptor"); } Object obj; try { obj = desc.isInstantiable() ? desc.newInstance() : null; } catch (Exception ex) { throw (IOException) new InvalidClassException( desc.forClass().getName(), "unable to create instance").initCause(ex); } passHandle = handles.assign(unshared ? unsharedMarker : obj); ClassNotFoundException resolveEx = desc.getResolveException(); if (resolveEx != null) { handles.markException(passHandle, resolveEx); } if (desc.isExternalizable()) { readExternalData((Externalizable) obj, desc); } else { readSerialData(obj, desc); } handles.finish(passHandle); if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) { Object rep = desc.invokeReadResolve(obj); if (unshared && rep.getClass().isArray()) { rep = cloneArray(rep); } if (rep != obj) { // Filter the replacement object if (rep != null) { if (rep.getClass().isArray()) { filterCheck(rep.getClass(), Array.getLength(rep)); } else { filterCheck(rep.getClass(), -1); } } handles.setObject(passHandle, obj = rep); } } return obj; }
boolean hasReadResolveMethod() { requireInitialized(); return (readResolveMethod != null); }
readResolveMethod = getInheritableMethod( cl, "readResolve", null, Object.class);
Object invokeReadResolve(Object obj) throws IOException, UnsupportedOperationException { requireInitialized(); if (readResolveMethod != null) { try { return readResolveMethod.invoke(obj, (Object[]) null); } catch (InvocationTargetException ex) { Throwable th = ex.getTargetException(); if (th instanceof ObjectStreamException) { throw (ObjectStreamException) th; } else { throwMiscException(th); throw new InternalError(th); // never reached } } catch (IllegalAccessException ex) { // should not occur, as access checks have been suppressed throw new InternalError(ex); } } else { throw new UnsupportedOperationException(); } }
七、注册式单例
//枚举单例 public enum EnumEinheit { INSTANCE; private Object data; public Object getData(){ return data; } public void setData(Object data){ this.data=data; } public static EnumEinheit getInstance(){ return INSTANCE; } }
public class EnumEinheitTest { public static void main(String[] args) { try { EnumEinheit enumEinheit=null; EnumEinheit enumEinheit1=EnumEinheit.getInstance(); enumEinheit1.setData(new Object()); FileOutputStream fos=new FileOutputStream("ghy"); ObjectOutputStream oos=new ObjectOutputStream(fos); oos.writeObject(enumEinheit1); oos.flush(); oos.close(); FileInputStream fis=new FileInputStream("ghy"); ObjectInputStream ois=new ObjectInputStream(fis); enumEinheit= (EnumEinheit) ois.readObject(); ois.close(); System.out.println(enumEinheit==enumEinheit1); }catch (Exception e){ e.printStackTrace(); } } }
这代码我就不运行了,结果一定是true,这说明他们俩是一个实例,从而证明这是一个单例,下面通过分析源码来看下他是怎么做到的。在看源码前需要下载一个Jad工具进行反编译 通过工具可以很快发现下面代码
static
{
INSTANCE = new EnumSingleton("INSTANCE", 0);
$VALUES = (new EnumSingleton[] {
INSTANCE
});
}
从上面可以看出,枚举式单例在静态代码块中就给 INSTANCE 进行了赋值,是饿汉式单例的实现,这里面说明了饿汉的问题权举也有。至此,还有另一个问题要解决,那就是序列化为什么破坏不了单例,不妨再来看一下 JDK源码,还是回到 ObjectInputStream 的 readObject0()方法:
private Object readObject0(boolean unshared) throws IOException { boolean oldMode = bin.getBlockDataMode(); if (oldMode) { int remain = bin.currentBlockRemaining(); if (remain > 0) { throw new OptionalDataException(remain); } else if (defaultDataEnd) { /* * Fix for 4360508: stream is currently at the end of a field * value block written via default serialization; since there * is no terminating TC_ENDBLOCKDATA tag, simulate * end-of-custom-data behavior explicitly. */ throw new OptionalDataException(true); } bin.setBlockDataMode(false); } byte tc; while ((tc = bin.peekByte()) == TC_RESET) { bin.readByte(); handleReset(); } depth++; totalObjectRefs++; try { switch (tc) { case TC_NULL: return readNull(); case TC_REFERENCE: return readHandle(unshared); case TC_CLASS: return readClass(unshared); case TC_CLASSDESC: case TC_PROXYCLASSDESC: return readClassDesc(unshared); case TC_STRING: case TC_LONGSTRING: return checkResolve(readString(unshared)); case TC_ARRAY: return checkResolve(readArray(unshared)); case TC_ENUM: return checkResolve(readEnum(unshared)); case TC_OBJECT: return checkResolve(readOrdinaryObject(unshared)); case TC_EXCEPTION: IOException ex = readFatalException(); throw new WriteAbortedException("writing aborted", ex); case TC_BLOCKDATA: case TC_BLOCKDATALONG: if (oldMode) { bin.setBlockDataMode(true); bin.peek(); // force header read throw new OptionalDataException( bin.currentBlockRemaining()); } else { throw new StreamCorruptedException( "unexpected block data"); } case TC_ENDBLOCKDATA: if (oldMode) { throw new OptionalDataException(true); } else { throw new StreamCorruptedException( "unexpected end of block data"); } default: throw new StreamCorruptedException( String.format("invalid type code: %02X", tc)); } } finally { depth--; bin.setBlockDataMode(oldMode); } }
从上面可以看到在 readObject0()中调用了 readEnum()方法,来看 readEnum()中代码实现:
private Enum<?> readEnum(boolean unshared) throws IOException { if (bin.readByte() != TC_ENUM) { throw new InternalError(); } ObjectStreamClass desc = readClassDesc(false); if (!desc.isEnum()) { throw new InvalidClassException("non-enum class: " + desc); } int enumHandle = handles.assign(unshared ? unsharedMarker : null); ClassNotFoundException resolveEx = desc.getResolveException(); if (resolveEx != null) { handles.markException(enumHandle, resolveEx); } String name = readString(false); Enum<?> result = null; Class<?> cl = desc.forClass(); if (cl != null) { try { @SuppressWarnings("unchecked") Enum<?> en = Enum.valueOf((Class)cl, name); result = en; } catch (IllegalArgumentException ex) { throw (IOException) new InvalidObjectException( "enum constant " + name + " does not exist in " + cl).initCause(ex); } if (!unshared) { handles.setObject(enumHandle, result); } } handles.finish(enumHandle); passHandle = enumHandle; return result; }
public class EnumEinheitTest { public static void main(String[] args) { // try { // EnumEinheit enumEinheit=null; // EnumEinheit enumEinheit1=EnumEinheit.getInstance(); // enumEinheit1.setData(new Object()); // // FileOutputStream fos=new FileOutputStream("ghy"); // ObjectOutputStream oos=new ObjectOutputStream(fos); // oos.writeObject(enumEinheit1); // oos.flush(); // oos.close(); // // FileInputStream fis=new FileInputStream("ghy"); // ObjectInputStream ois=new ObjectInputStream(fis); // enumEinheit= (EnumEinheit) ois.readObject(); // ois.close(); // // System.out.println(enumEinheit==enumEinheit1); // // }catch (Exception e){ // e.printStackTrace(); // } try { Class clazz=EnumEinheit.class; Constructor c = clazz.getDeclaredConstructor(); c.newInstance(); }catch (Exception e){ e.printStackTrace(); } } }
报的错意思是没找到无参的构造方法。这时候,打开 java.lang.Enum 的源码代码,查看它的构造方法,只有一个 protected的构造方法,代码如下:
protected Enum(String name, int ordinal) { this.name = name; this.ordinal = ordinal; }
根据上面情况,那下面试下强吻能能访问的到
public class EnumEinheitTest { public static void main(String[] args) { // try { // EnumEinheit enumEinheit=null; // EnumEinheit enumEinheit1=EnumEinheit.getInstance(); // enumEinheit1.setData(new Object()); // // FileOutputStream fos=new FileOutputStream("ghy"); // ObjectOutputStream oos=new ObjectOutputStream(fos); // oos.writeObject(enumEinheit1); // oos.flush(); // oos.close(); // // FileInputStream fis=new FileInputStream("ghy"); // ObjectInputStream ois=new ObjectInputStream(fis); // enumEinheit= (EnumEinheit) ois.readObject(); // ois.close(); // // System.out.println(enumEinheit==enumEinheit1); // // }catch (Exception e){ // e.printStackTrace(); // } try { Class clazz=EnumEinheit.class; Constructor c = clazz.getDeclaredConstructor(String.class,int.class); c.setAccessible(true); c.newInstance(); }catch (Exception e){ e.printStackTrace(); } } }
结果如下
@CallerSensitive public T newInstance(Object ... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<?> caller = Reflection.getCallerClass(); checkAccess(caller, clazz, null, modifiers); } } if ((clazz.getModifiers() & Modifier.ENUM) != 0) throw new IllegalArgumentException("Cannot reflectively create enum objects"); ConstructorAccessor ca = constructorAccessor; // read volatile if (ca == null) { ca = acquireConstructorAccessor(); } @SuppressWarnings("unchecked") T inst = (T) ca.newInstance(initargs); return inst; }
public class Container { private Container(){} private static Map<String,Object> ioc=new ConcurrentHashMap<String,Object>(); public static Object getBean(String className){ synchronized (ioc){ if (!ioc.containsKey(className)){ Object obj=null; try { obj = Class.forName(className).newInstance(); ioc.put(className, obj); }catch (Exception e){ e.printStackTrace(); } return obj; }else { return ioc.get(className); } } } }
八、ThreadLocal 线程单例
这个玩意我本来不想说的,不明白的可以看我多线程文章就很清楚的懂了,在这里之所以列出来,是因为在当时讲解多线程时有人问我应用场景,这种线程单例应用 最多的还是连接池,比喻数据库数据源切换时的线程单例,不说那么多,反正这写法很多人也用不上,就简单说明下
public class ThreadLocalSingleton { private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance = new ThreadLocal<ThreadLocalSingleton>(){ @Override protected ThreadLocalSingleton initialValue() { return new ThreadLocalSingleton(); } }; private ThreadLocalSingleton(){} public static ThreadLocalSingleton getInstance(){ return threadLocalInstance.get(); } }
九、单例模式在开源代码中的应用
1、JDK 中 java.lang.Runtime 类,每个运行中的 Java 应用的环境信息,单例。
public class Runtime { private static Runtime currentRuntime = new Runtime(); public static Runtime getRuntime() { return currentRuntime; } private Runtime() {} . . . }
2、Spring 的单例 bean 的实现
ApplicationContext context = new ClassPathXmlApplicationContext("beanFactoryTest.xml"); MyTestBean bean1 = (MyTestBean)context.getBean("myTestBean"); MyTestBean bean2 = (MyTestBean)context.getBean("myTestBean"); System.out.println(bean1 == bean2); // 打印true ApplicationContext context2 = new ClassPathXmlApplicationContext("beanFactoryTest.xml"); MyTestBean bean3 = (MyTestBean)context2.getBean("myTestBean");
代码中 bean1 = bean2,因为 Spring 默认是单例,且 bean1 和 bean2 都取自 context 实例中容器;
bean1 != bean3,因为 bean1 和 bean3 分别取自不同的容器。
那 Spring 是如何实现单例的呢?
看下 org.springframework.beans.factory.support.AbstractBeanFactory 获取 bean 的代码
protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { . . . //获取缓存的 bean 实例 Object sharedInstance = getSingleton(beanName); . . . //创建 bean 实例 if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } . . . }
1、Srping IoC 容器在初始化的时候,若未配置懒加载,单例 bean 实例会在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory 创建,createBean -> doCreateBean -> createBeanInstance
2、单例 bean 实例获取的逻辑在 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry 的 getSingleton 方法
public class DefaultSingletonBeanRegistry { //缓存单例 bean 实例 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256); protected Object getSingleton(String beanName, boolean allowEarlyReference) { //从 ConcurrentHashMap 中获取 bean 实例 Object singletonObject = this.singletonObjects.get(beanName); . . . return (singletonObject != NULL_OBJECT ? singletonObject : null); } }
可见 Spring 的单例 bean 实例的创建,是通过代码自己控制的,单例 bean 实例保存在一个 ConcurrentHashMap 中,而不是上述单例模式的典型实现方式。
这里说的 Spring 的单例是在 IoC 容器中,实际开发中有时候还需要考虑线程间的单例和分布式中多进程间的单例。线程间的单例可以使用 ThreadLocal 实现,进程间的单例要借助分布式锁以及对单例实例进行序列化存储与反序列化。
十、总结
git源码:https://gitee.com/TongHuaShuShuoWoDeJieJu/design_pattern.git