详解单例模式及其在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方法就是将对象加入到单例注册表中:

(五)总结

至此,单例模式的内容差不多就结束了,结合源码看设计模式每次都有新收获。我是鱼仔,我们下期再见!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java鱼仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值