如何为外部库类创建单例对象?

问题描述 投票:0回答:3

有没有更好的方法为外部类创建单例对象?我正在这样做,但是有更好的方法吗?

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;
    }
}
java design-patterns
3个回答
0
投票

在这种情况下,您的外部库类不是单例,因为首先它是一个接口。即使不是,任何人都可以从此类外部创建一个新实例。您无法将不是单例的外部类转变为一个。

现在,如果您想为自己的类 GetAWSCredentials 找到创建单例的最简洁方法,那么您可以尝试 effective-java 建议


0
投票

是的,有。简单来说就是:

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个实例)。
  • 上面使用了 java itself 具有内置锁定机制的事实:保证类永远不会加载多次,因此,通过立即初始化
    static final
    字段,我们可以搭载它,这是该 java 提供的最有效的机制。
  • 第二个片段之所以有效,是因为在有人调用
    .getInstance()
    之前,该内部类从未被触及。只要该内部类未被触及,它就不会被初始化。即使
    new AwsCredentialsProvider()
    需要一个小时,直到有人打电话给
    .getInstance()
    才花掉这个小时。

注意:

  • 正确的名字是
    AwsCredentials
    ,而不是
    AWSCredentials
    。在 Java 中,命名约定规定首字母缩略词仍应小写。是
    DvdPlayer
    ,不是
    DVDPlayer
  • 您在这里使用单例可能是不正确的。例如,测试场景肯定会想要替换这些凭据。

0
投票

首先创建一个客户端类。

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。因为它是单例。

© www.soinside.com 2019 - 2024. All rights reserved.