AWS SDK for Java v2 开发规范:优先使用静态工厂方法而非构造器(Favor Static Factory Methods)
【免费下载链接】aws-sdk-java-v2The official AWS SDK for Java - Version 2项目地址: https://gitcode.com/GitHub_Trending/aw/aws-sdk-java-v2
静态工厂方法(Static Factory Method)是 AWS SDK for Java v2 中初始化类实例的核心约定,本指南基于 docs/guidelines/FavorStaticFactoryMethods.md 展开,并深入结合仓库源码进行验证与扩充。阅读本文后,你将掌握 SDK 中create()、builder()、defaultXXX()等工厂方法的命名约定、设计动机与底层实现原理,能够在自己的服务客户端、凭据提供者与配置类设计中正确套用这一模式。
该规范属于 AWS SDK for Java v2 Development Guidelines 指南系列之一(Status: Accepted),与 ClientConfiguration.md(配置对象不可变性与 Builder 接口)、NamingConventions.md 共同构成了 SDK 公共 API 的设计骨架。
为什么优先使用静态工厂方法而不是构造器
SDK 开发指南开宗明义:在一般情况下,应优先使用静态工厂方法而非构造器来初始化一个类。这一约定并非凭空规定,而是基于以下三点可验证的设计收益。
1. 工厂方法名携带语义,可读性显著提升
构造器与类名相同,无法表达"这个对象是用什么参数、什么意图创建的"。静态工厂方法可以用动词短语直接描述返回对象的语义。指南中的对比示例:
// 使用静态工厂方法:一眼看出"正在创建一个使用默认设置的 foobar provider" FoobarProvider defaultProvider = FoobarProvider.defaultFoobarProvider(); // 使用构造器:看不出任何默认行为信息 FoobarProvider defaultProvider = new FoobarProvider();从源码结构看,这一理念在 SDK 中贯彻到了几乎所有公共入口类上。例如默认凭据提供者 DefaultCredentialsProvider.java 提供builder()作为唯一公开的实例化入口,而create()则明确表达"返回默认配置的单例"这一语义。
2. 配合不可变类实现实例复用
静态工厂方法非常适合不可变类:既然对象一旦创建就不会改变,就可以缓存并复用同一个实例,避免每次调用都新建对象。SDK 中最典型的例子就是单例缓存:
// DefaultCredentialsProvider 中缓存的默认单例 private static final DefaultCredentialsProvider DEFAULT_CREDENTIALS_PROVIDER = new DefaultCredentialsProvider(builder()); public static DefaultCredentialsProvider create() { return DEFAULT_CREDENTIALS_PROVIDER; }对应源码见 DefaultCredentialsProvider.java:DEFAULT_CREDENTIALS_PROVIDER在类加载时被构造一次,之后每次调用create()都返回同一个实例,避免了重复构建凭据链的开销。
3. 工厂方法可以返回任意子类型,解放实现灵活性
静态工厂方法声明的返回类型可以是接口或父类,而方法内部可以返回任何满足要求的子类型。这意味着 SDK 可以在不破坏 API 的前提下,将实现类隐藏在包内部,或按需切换不同实现。
BackoffStrategy是这一点的绝佳证据。SDK 同时存在两代实现:
- 老一代接口 core/sdk-core/src/main/java/software/amazon/awssdk/core/retry/backoff/BackoffStrategy.java 的
defaultStrategy()根据当前RetryMode(LEGACY / STANDARD / ADAPTIVE)返回不同的FullJitterBackoffStrategy或EqualJitterBackoffStrategy实例; - 新一代接口 core/retries-spi/src/main/java/software/amazon/awssdk/retries/api/BackoffStrategy.java 更是把工厂方法直接定义在接口上:
retryImmediately()、fixedDelay(Duration)、fixedDelayWithoutJitter(...)、exponentialDelay(...)、exponentialDelayHalfJitter(...)、exponentialDelayWithoutJitter(...),分别返回Immediately、FixedDelayWithJitter、ExponentialDelayWithJitter等隐藏在internal包下的实现类。
调用方只面对BackoffStrategy接口,完全不感知具体实现类,这正是"返回任意子类型"带来的 API 稳定性红利。
静态工厂方法的两点代价与规避方式
指南也坦诚列出了静态工厂方法相对构造器的缺点,并给出了可落地的解决方案:
可发现性不如构造器:用户习惯了
new,可能不清楚该类提供了哪些工厂方法。规避手段有两条:其一,SDK 统一约定一组常见方法名(如create()),让用户在多个类间迁移时零学习成本;其二,依赖 IntelliJ、Eclipse 等智能 IDE 的自动补全与提示能力。无法被继承:没有 public/protected 构造器的类不能被子类化。但这并非缺陷——它反向推动开发者优先使用组合(composition)而非继承(inheritance)。SDK 中大量
final类(如DefaultCredentialsProvider本身就是public final class)正是这一权衡的体现:通过 Builder 和组合链来扩展行为,而不是靠继承。
完整示例剖析:DefaultCredentialsProvider 的工厂方法设计
指南给出了 SDK 中DefaultCredentialsProvider的简化示例,仓库中 DefaultCredentialsProvider.java 的完整实现可拆解为以下四个要素:
@SdkPublicApi public final class DefaultCredentialsProvider implements AwsCredentialsProvider, SdkAutoCloseable, ToCopyableBuilder<DefaultCredentialsProvider.Builder, DefaultCredentialsProvider> { // 要素一:类加载期构建的默认单例 private static final DefaultCredentialsProvider DEFAULT_CREDENTIALS_PROVIDER = new DefaultCredentialsProvider(builder()); // 要素二:构造器是私有的,外部无法直接 new private DefaultCredentialsProvider(Builder builder) { this.providerChain = createChain(builder); } // 要素三:create() 返回默认配置单例(当前已标记 @Deprecated,见下文) @Deprecated public static DefaultCredentialsProvider create() { return DEFAULT_CREDENTIALS_PROVIDER; } // 要素四:builder() 返回可配置的 Builder public static Builder builder() { return new Builder(); } // Builder 必须是 public static final 的嵌套类 public static final class Builder implements CopyableBuilder<Builder, DefaultCredentialsProvider> { // profileFile / profileName / reuseLastProviderEnabled / asyncCredentialUpdateEnabled 等配置项 // ... } }四种创建实例的方式对比如下:
// 方式一:使用默认配置的单例(create 返回缓存实例) DefaultCredentialsProvider defaultCredentialsProvider1 = DefaultCredentialsProvider.create(); // 方式二:通过 Builder 自定义配置后构建(推荐) DefaultCredentialsProvider defaultCredentialsProvider2 = DefaultCredentialsProvider.builder().build();需要特别指出的一个演进细节:create()目前在源码中已被标记@Deprecated(见 DefaultCredentialsProvider.java),其 Javadoc 明确说明——返回单例的方式可能在某个客户端close()掉该 provider 时影响其他仍在使用的客户端,因此官方推荐改用builder().build()创建相互独立的实例。这恰好印证了指南中"工厂方法配合不可变对象复用"的收益与边界:单例复用省资源,但共享可变生命周期时需谨慎。
Builder 中四个可配置字段及其默认值(来自 DefaultCredentialsProvider.java):
| 配置项 | 默认值 | 说明 |
|---|---|---|
profileFile | 默认位置~/.aws/credentials | 自定义凭据配置文件,可传ProfileFile或Supplier<ProfileFile> |
profileName | 默认 profile | 指定使用的 profile 名称 |
reuseLastProviderEnabled | true | 是否复用凭据链中上一次成功的 provider,开启后解析凭据更快 |
asyncCredentialUpdateEnabled | false | 是否后台异步刷新凭据,开启后resolveCredentials()更少阻塞,但占用额外资源 |
底层createChain(builder)(DefaultCredentialsProvider.java)用工厂方法串联了六层凭据来源:SystemPropertyCredentialsProvider.create()→EnvironmentVariableCredentialsProvider.create()→WebIdentityTokenFileCredentialsProvider.builder().build()→ProfileCredentialsProvider.builder().build()→ContainerCredentialsProvider.builder().build()→InstanceProfileCredentialsProvider.builder().build()。可以看到,链上的每个 provider 同样遵循静态工厂方法约定,形成了 SDK 内部自洽的一致风格。
静态工厂方法的命名约定
指南将工厂方法名收敛为两类,避免各模块命名发散:
create()/create(params)—— 创建新实例
用于无歧义地"创建并返回一个新实例"。示例:DynamoDBClient.create()、DynamoDBClient.builder().build()。SDK 中所有服务客户端(DynamoDB、S3、SQS 等)均由代码生成器统一生成这一对入口,保证跨服务 API 形状一致。
这一约定在代码生成器中有直接证据:codegen 模块的 BaseClientBuilderClass.java 与 SyncClientClass.java 中大量通过$T.create()形式的 JavaPoet 代码块注入工厂调用(如默认 token provider、默认认证方案 provider、RequestChecksumCalculationResolver.create()等),说明**create()是代码生成器层面的硬编码规范**,而不仅是手工代码的习惯。
defaultXXX()—— 返回默认配置实例
用于"返回使用默认设置的单例或实例"。示例:BackoffStrategy.defaultStrategy()、BackoffStrategy.defaultThrottlingStrategy()。
以 core/sdk-core/src/main/java/software/amazon/awssdk/core/retry/backoff/BackoffStrategy.java 为例,defaultStrategy()是无参重载,内部委托给按RetryMode分发的defaultStrategy(RetryMode),构造FullJitterBackoffStrategy并填入来自SdkDefaultRetrySetting的默认 base delay 与MAX_BACKOFF上限。在测试代码 RetryPolicyTest.java 中,RetryPolicy默认行为被断言为BackoffStrategy.defaultStrategy(),证明该工厂方法确实是运行时默认值的事实来源;Ec2MetadataRetryPolicyTest.java 亦用同样的方式获取默认退避策略。
在实际编码中如何落地这套约定
结合指南与源码,可以在自己编写的 SDK 扩展或服务代码中按以下清单应用:
- 类设计层面:优先把类声明为
final,构造器设为private或package-private,通过组合扩展行为;对外只暴露public static工厂方法。 - 默认实例:若类有"零配置即可用"的语义,提供
public static Xxx defaultXxx(),并在内部用private static final字段缓存单例(前提是实例不可变且无共享生命周期风险)。 - 可配置实例:提供
public static Builder builder(),Builder 作为public static final嵌套类实现,build()返回新实例;需要深拷贝时可实现ToCopyableBuilder/CopyableBuilder(见 DefaultCredentialsProvider.java 的toBuilder())。 - 命名纪律:创建新实例用
create()/create(params),返回默认配置实例用defaultXXX(),不要自创getInstance()、newInstance()等变体,保持与 SDK 全库风格统一。 - 单例与生命周期权衡:当单例实例拥有可关闭的资源(如凭据 provider 实现
SdkAutoCloseable)时,要评估共享关闭带来的影响,必要时推荐用户通过builder().build()获取独立实例——这正是DefaultCredentialsProvider.create()被弃用的原因。
总结
Favor Static Factory Methods是 AWS SDK for Java v2 公共 API 设计中最基础的约定之一:它以有语义的方法名、不可变对象的实例复用、返回子类型的实现灵活性三项收益换取了两点可控的代价(可发现性与不可继承性),并通过统一的create()/defaultXXX()命名让整套 SDK 的用户心智保持一致。从DefaultCredentialsProvider的私构造器 + 单例缓存 + Builder 四要素,到BackoffStrategy接口上的静态工厂家族,再到 codegen 生成器中硬编码的$T.create()调用,仓库源码处处印证着这一规范的落地深度。理解并遵循这一约定,是读懂 SDK 公共 API、编写风格一致的扩展代码的前提。
【免费下载链接】aws-sdk-java-v2The official AWS SDK for Java - Version 2项目地址: https://gitcode.com/GitHub_Trending/aw/aws-sdk-java-v2
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考