有没有更好的方法为外部类创建单例对象?我正在这样做,但是有更好的方法吗?
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
public class GetAWSCredentials {
private static AWSCredentialsProvider credentials;
public static synchronized AWSCredentialsProvider getInstance() {
if (credentials == null) {
credentials = new DefaultAWSCredentialsProviderChain();
}
return credentials;
}
}
在这种情况下,您的外部库类不是单例,因为首先它是一个接口。即使不是,任何人都可以从此类外部创建一个新实例。您无法将不是单例的外部类转变为一个。
现在,如果您想为自己的类 GetAWSCredentials 找到创建单例的最简洁方法,那么您可以尝试 effective-java 建议
是的,有。简单来说就是:
public class GetAwsCredentials {
private static final AwsCredentialsProvider credentials = new AwsCredentialsProvider();
public static AwsCredentialsProvider getInstance() {
return credentials;
}
}
您可能会想:哦不,那不行!我不希望在 JVM 启动时创建此实例,仅在需要时创建!
但是..这已经是java的工作原理了。直到需要时才会按原样加载类。在某种访问方法中创建实例实际上是没有意义的;只需在类负载上进行即可。在 99% 需要单例的情况下,如果你要提到相关的类,你很快就会调用
getInstance()
方法。
在极不可能的情况下,您确实需要这样做(并且如果该构造函数做了相当多的繁重工作,则这仅才算数),您可以使用此构造:
public class GetAwsCredentials {
private static class AwsCredentialsHolder {
static final AwsCredentialsProvider provider = new AwsCredentialsProvider();
}
public static AwsCredentialsProvider getInstance() {
return AwsCredentialsHolder.provider;
}
}
无需使用
volatile
或 synchronized
。也就是说:
synchronized
),要么试图减轻重量,例如使用双重检查锁定,“太轻” - 它们似乎可以工作,但在适当的负载下它们会失败(从某种意义上说,您最终会得到2个实例)。static final
字段,我们可以搭载它,这是该 java 提供的最有效的机制。.getInstance()
之前,该内部类从未被触及。只要该内部类未被触及,它就不会被初始化。即使 new AwsCredentialsProvider()
需要一个小时,直到有人打电话给 .getInstance()
才花掉这个小时。注意:
AwsCredentials
,而不是AWSCredentials
。在 Java 中,命名约定规定首字母缩略词仍应小写。是DvdPlayer
,不是DVDPlayer
。首先创建一个客户端类。
public class AwsClient {
private volatile static AwsClient instance;
public String key;
public String secret;
private AwsClient() {
}
public static AwsClient getInstance() {
if (instance == null) {
synchronized (AwsClient.class) {
if (instance == null) {
instance = new AwsClient();
}
}
}
return instance;
}
public void setEnv(String key, String secret) {
this.key = key;
this.secret = secret;
}
public AWSSecretsManager getAWSSecretsManager(Regions regions, ClientConfiguration config) {
assert StringUtils.isNotEmpty(key);
assert StringUtils.isNotEmpty(secret);
return AWSSecretsManagerClientBuilder.standard()
.withRegion(regions)
.withCredentials(new AWSStaticCredentialsProvider(getAwsCredentials()))
.withClientConfiguration(config)
.build();
}
private AWSCredentials getAwsCredentials() {
Assert.notNull(key, "key not null");
Assert.notNull(secret, "secret not null");
return new BasicAWSCredentials(key, secret);
}
}
第二,构建一个使用类。
public class AwsSecretManageUtil {
private final static Map<String, Map<String, String>> CACHE = new HashMap<>();
public static String getVal(String secretName, String secretKey) {
if (CACHE.containsKey(secretName)) {
if (CACHE.get(secretName).containsKey(secretKey)) {
return CACHE.get(secretName).get(secretKey);
}
}
AWSSecretsManager awsSecretsManager = AwsClient.getInstance().getAWSSecretsManager();
GetSecretValueRequest secretValueRequest = new GetSecretValueRequest().withSecretId(secretName);
GetSecretValueResult secretValueResponse = awsSecretsManager.getSecretValue(secretValueRequest);
Map<String, String> map = JSONObject.parseObject(secretValueResponse.getSecretString(), new TypeReference<Map<String, String>>(){});
CACHE.put(secretName, map);
return map.get(secretKey);
}
}
使用后。
AwsClient.getInstance().setEnv(key, secret);
String key = AwsSecretManageUtil.getVal("dev/s3", "key");
如果秘钥没有改变,则只需setEnv一次。请不要多次设置Env。因为它是单例。