详解单例模式及其在Sping中的最优实践
Posted Java鱼仔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详解单例模式及其在Sping中的最优实践相关的知识,希望对你有一定的参考价值。
(一)什么是单例模式
在程序中,每new() 一个对象,就会有一个对象实例生成。有时候在程序中,需要有一个在完整运行状态下只需要生成一个的实例,我们把这种实例称为单例。
抽象到设计模式中,这种只生成一个实例的模式就是单例模式(Singleton)。
(二)单例模式的实现
单例模式的实现有很多种方式,归根到底是保证new Class()的操作只做一次,在大多数的实现上,会将需要实现单例的类的构造方法改为private修饰,使得无法通过new命令创建对象实例。
(三)单例模式的代码实现
代码模式有很多种实现方式,主要有饿汉式、懒汉式以及有点特殊的单例注册表(Spring的实现方式)。下面会针对上面的这三种方式各自给出一种实现方式。
3.1 饿汉式
饿汉式简单来说就是在项目启动时就将对象实例创建出来,可以用来做需要执行一次的操作:
public class HungrySingleton
public static final HungrySingleton INSTANCE;
static
INSTANCE=new HungrySingleton();
private HungrySingleton()
调用时只需要直接调用INSTANCE变量就行:
HungrySingleton instance = HungrySingleton.INSTANCE;
静态代码块饿汉式适合从外部文件中获取数据时使用,我在项目下新建一个info.properties,里面包含一条内容info=hello。饿汉式单例代码如下:
public class HungrySingleton2
public static final HungrySingleton2 INSTANCE;
private String info;
static
Properties properties=new Properties();
try
properties.load(HungrySingleton2.class.getClassLoader().getResourceAsStream("info.properties"));
catch (IOException e)
e.printStackTrace();
INSTANCE=new HungrySingleton2(properties.getProperty("info"));
private HungrySingleton2(String info)
this.info=info;
public String getInfo()
return info;
public void setInfo(String info)
this.info = info;
@Override
public String toString()
return "Singleton3" +
"info='" + info + '\\'' +
'';
public static void main(String[] args)
HungrySingleton2 instance = HungrySingleton2.INSTANCE;
System.out.println(instance.getInfo());
3.2 懒汉式
懒汉式是指当第一次调用时才会生成这个实例,后面再调用就只会使用之前生成的实例。懒汉式是实际编写单例模式代码时用的比较多的方案,下面给出一段十分经典的懒汉式单例模式代码案例:
public class LazySingleton
private static volatile LazySingleton Instance;
private LazySingleton();
public static LazySingleton getInstance()
if (Instance==null)
synchronized (LazySingleton.class)
if(Instance==null)
Instance=new LazySingleton();
return Instance;
在面试中,往往这道题能引出volatile和synchorized这两个知识点。
3.3 单例注册表
我看网上很少有人介绍这种单例模式,单例注册表是Spring中Bean单例的核心实现方案。可以通过一个ConcurrentHashMap存储Bean对象,保证Bean名称唯一的情况下也能保证线程安全。下面是单例注册表的简单实现:
public class RegSingleton
private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(16);
private RegSingleton()
public static Object getInstance(String className)
if (StringUtils.isEmpty(className))
return null;
if (singletonObjects.get(className) == null)
try
singletonObjects.put(className, Class.forName(className).newInstance());
catch (Exception e)
e.printStackTrace();
return singletonObjects.get(className);
新建一个测试类:
public class User
private String id;
public void setId(String id)
this.id = id;
public String getId()
return id;
@Override
public boolean equals(Object o)
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return Objects.equals(getId(), user.getId());
@Override
public int hashCode()
return Objects.hash(getId());
最后测试单例是否生效:
public class Main
public static void main(String[] args)
User user1 = (User) RegSingleton.getInstance("com.javayz.singleton.User");
User user2 = (User) RegSingleton.getInstance("com.javayz.singleton.User");
System.out.println(user1==user2);
(四)单例模式在Spring中的最佳实践
单例模式的最佳实践就是Spring中的Bean了,Spring中的Bean默认都是单例模式,Spring实现单例的方式就采用了单例注册表的方式。
首先在Bean工厂中,如果设置了Spring的Bean模式为单例模式,Spring就会通过getSingleton的方式去获取单例Bean对象。
接着就会进入到DefaultSingletonBeanRegistry类的getSingleton方法中,忽略掉其他代码,只看单例Bean生成相关代码
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)
//一系列处理操作
finally
if (recordSuppressedExceptions)
this.suppressedExceptions = null;
afterSingletonCreation(beanName);
if (newSingleton)
//如果实例对象不存在,注册到单例注册表中
addSingleton(beanName, singletonObject);
return singletonObject;
对应的addSingleton方法就是将对象加入到单例注册表中:
(五)总结
至此,单例模式的内容差不多就结束了,结合源码看设计模式每次都有新收获。我是鱼仔,我们下期再见!
以上是关于详解单例模式及其在Sping中的最优实践的主要内容,如果未能解决你的问题,请参考以下文章