java

java 枚举类实现的单例与 spring boot 枚举类依赖注入

我们知道,在单例的诸多实现里,枚举类实现的单例是最好的。在 spring boot 应用里,我们也可以使用枚举类来做单例,但是可能会遇到依赖注入的问题,这个问题呢,其实也不是问题,但肯定会困扰不少新手。本文就此展开,先讨论各种单例的优劣,再讨论 spring boot 里 枚举类的依赖注入。

1、最基础的单例,懒汉式,线程不安全,不推荐!!!

private static Test test;

private Test() {
}

public static Test getInstance() {
    if (test == null) {
        test = new Test();
    }
    return test;
}

上面的代码线程不安全,假设同时有多个线程进来,可能都执行到 test == null 这一行,发现是true,然后创建实例,最后每个线程获取到的实例可能不是一样的,即没有做到单例!

2、使用 synchronized 关键字的懒汉模式,既然上述代码线程不安全,那么,我们加个 synchronized 关键字,然后相关代码变为:

public static synchronized Test getInstance() {
    if (test == null) {
        test = new Test();
    }
    return test;
}

这种写法是解决了上述的并发问题,但是每个线程都是阻塞的,即这个函数同时只有一个线程可访问,这将多核变为了单核。。。

3、懒汉模式 + double check(双重检查),代码变为:

private volatile static Test test;

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

既然每次都给方法加锁很慢,那么,我们可以在该对象为空时才加锁,这样只在第一次创建时会有锁,以后就不用了,性能大幅提升!至于这里的 volatile 关键字,以后有空再说吧。

4、饿汉模式

实现单例,还可以这样:

private static final Test test = new Test();

public static Test getInstance() {
    return test;
}

这种方式的缺点是没有懒加载,如果确定不需要懒加载,则可以使用这种方式。

5、匿名内部类

private static class TestHold {
    private static final Test test = new Test();
}

public static Test getInstance() {
    return TestHold.test;
}

这种方式和饿汉模式很像,不过使用了一个匿名内部类,保证不会在调用该类时立即初始化,而是懒加载。

6、枚举方式,这也是本人非常强力推荐的方式:

public enum Test {

    INSTANCE;
    
    public void doSomethings() {
        
    }
}

那么,问题来了,在 spring boot 应用里,我们应该如何在 枚举类里做依赖注入呢?毕竟它只是个枚举类啊!其实完全可以如此这样!

public enum SurveyResultProvider {

    COVID19(Constant.SurveyCodeKey.COVID19),
    ONE_X(Constant.SurveyCodeKey.ONE_X);

    private final String surveyCodeKey;
    private SurveyResultHandler surveyResultHandler;

    SurveyResultProvider(String surveyCodeKey) {
        this.surveyCodeKey = surveyCodeKey;
    }

    public static SurveyResultHandler getSurveyResultHandler(String surveyCode) {
        for (SurveyResultProvider handler : SurveyResultProvider.values()) {
            if (surveyCode.equals(Configuration.getString(handler.surveyCodeKey))) {
                return handler.surveyResultHandler;
            }
        }
        return null;
    }

    @Component
    public static class ServiceInjector {

        public ServiceInjector(@Qualifier("onexResult") SurveyResultHandler oneXResultHandler,
                               @Qualifier("covid19Result") SurveyResultHandler surveyResultHandler) {
            ONE_X.surveyResultHandler = oneXResultHandler;
            COVID19.surveyResultHandler = surveyResultHandler;
        }
    }
}

唯一让人觉得遗憾的是,这样做将不再是懒加载了,不过也好,服务端追求的应该是稳定性,而非内存占用!

full-stack-trip

Share
Published by
full-stack-trip

Recent Posts

Android 自定义 View 入门

说来惭愧,工作数年,连基本的自…

4 年 ago

retrofit 同时支持 xml 和 json

retrofit 解析 jso…

4 年 ago

mysql - 存储过程 从入门到放弃

最近有个报表的需求,于是乎用了…

4 年 ago

奶嘴战略 - 你不得不知道的扎心真相(一)

一句:英雄枯骨无人问,戏子家事…

4 年 ago

acme.sh 的简单使用

acme.sh 是纯 shel…

4 年 ago

wrk -更现代化的http压测工具

wrk 是一款更现代化的 ht…

4 年 ago