SpringBoot 实现单例模式遇到问题笔记

5,527 阅读1分钟

1.背景

后台服务要求集成一个第三方短信系统(云片:
github.com/yunpian/yun…)第三方规范要求使用单例模式。
第一版代码如下:

public class GetYunpianClient {
    private static YunpianClient yunpianClient = null;
    private GetYunpianClient() {
    }
    
    public static YunpianClient getInstance() throws Exception {
        if (yunpianClient == null) {
            synchronized (YunpianClient.class) {
                if (yunpianClient == null) {
                    yunpianClient = new YunpianClient("b596ec6ea87fcaa9250f72fbe335ed1a").init();
                }
            }
        }
        return yunpianClient;
    }
}

2.报错:Parameter 0 of constructor in xxx required a bean of type 'java.lang.String' that could not be found.

本来以双重检查方式实现单例模式,后领导不满意,要求改为Spring注入方式
于是脑抽就有了以下错误代码(构造注入):

@Service
public class GetYunpianClient {
    private YunpianClient yunpianClient;

    @Autowired
    public GetYunpianClient(String apiKey){
        this.yunpianClient = new YunpianClient(apiKey).init();
    }
    
    public YunpianClient init() {
        return yunpianClient;
    }
}

启动服务:报错如下:

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in com.zhexinit.callshow.app.common.util.GetYunpianClient required a bean of type 'java.lang.String' that could not be found.


Action:

Consider defining a bean of type 'java.lang.String' in your configuration.

由报错信息很容易看出:是由于类中定义了含参数的构造函数,Spring自动构造和注入时未为该Bean传入参数,引起报错(@Service注解默认注入的是不带参的构造函数).
解决方案:
1.使用@Configuration+ @Bean注解来实现注入
2.改为无参构造函数
选用第二种方案:

@Service
public class GetYunpianClient {
    private YunpianClient yunpianClient;
    @Value("${yunpian.apiKey}")
    private String apiKey;
    @Autowired
    public GetYunpianClient(){
        this.yunpianClient = new YunpianClient(apiKey).init();
    }
    public YunpianClient init() {
        return yunpianClient;
    }
}

又出现问题:@Value注解不生效,apiKey=null;

3.@Value注解不生效

原因:Java类先执行构造方法,再给@Value/@Autowired注解变量注入值
解决方案:
1.硬编码apiKey
2.改为get set注入
选用第二种方案如下:

@Service
public class GetYunpianClient {
    private YunpianClient yunpianClient;

    @Value("${yunpian.apiKey}")
    private String apiKey;

    @Autowired
    public YunpianClient getYunpianClient() {
        return yunpianClient;
    }

    @Autowired
    public void setYunpianClient() {
        this.yunpianClient = new YunpianClient(apiKey).init();
    }
}

4.【附】使用@Configuration+ @Bean注解来实现注入

@Configuration
public class YunpianClientConfig {
    @Value("${yunpian.apiKey}")
    private String apiKey;
    @Bean
    YunpianClient getYunpianClient(){
        return new YunpianClient(apiKey).init();
    }
}