OSS Java SDK V2 是对 V1(aliyun-sdk-oss)的重构版本,改造面集中在包名与 Maven 坐标、客户端创建方式和接口调用形态:客户端由静态工厂创建且必须指定地域,每个接口统一接受类型化的 Request 对象。行为上,V2 默认使用 V4 签名,并提供异步客户端。本文说明从 V1 迁移到 V2 需要处理的破坏性变更,并给出端到端的迁移流程。
变更范围评估
迁移前先按下面三类变更评估现有代码的改造范围:编译期就会暴露的阻塞项、编译能通过但行为发生变化的项、以及可选采用的新能力。V1 与 V2 的差异以本节三张表为准,其余章节只讲改造动作,不重复罗列差异。
编译期阻塞项
这些变更会直接导致编译失败,编译器可以逐个定位,改造范围相对确定。
变更项 | V1 | V2 | 对应步骤 |
Maven 坐标 |
|
| 第一步 |
JAXB 附加依赖 | Java 9 及以上需手动添加 | 不需要 | 第一步 |
Java 版本要求 | Java 7 及以上 | Java 8 及以上 | 迁移准备 |
包名 |
|
| 第二步 |
客户端创建 |
|
| 第三步 |
客户端配置 |
| Builder 链式方法 | 第三步 |
API 入参 | 同一操作提供多个简单参数重载 | 单一类型化 Request 对象(如 | 第四步 |
API 出参 |
|
| 第四步 |
异常模型 |
| 捕获 | 第五步 |
资源释放 |
| 实现 | 第三步 |
出参类型变更是唯一需要额外人工核对的一项:类型不匹配会在编译期报错,但字段是否读取正确编译期无法完全暴露,必须在第六步按核心链路逐项核对。
运行期行为变化
这些变更不会阻塞编译,但会改变请求的鉴权方式与目标域名,漏改的后果在运行期才显现。
变更项 | V1 | V2 | 对应步骤 |
签名算法 | 默认 V1 签名,使用 V4 签名需显式设置 |
| 第三步 |
地域与访问域名 |
|
| 第三步 |
可选新增能力
以下两项为非破坏性的新增能力,可按需采用,不采用不影响迁移完成。
变更项 | V1 | V2 | 对应步骤 |
异步能力 | 无独立异步客户端 |
| 第三步(可选) |
列举翻页 | 手动维护 marker 循环拉取 | 分页器接口,如 | 第四步(可选) |
迁移准备
开始改造前一次性确认以下五项,避免改到中途因环境或信息缺失返工。
构建环境:确认构建与运行环境的 JDK 版本满足 V2 的要求,即 Java 8 及以上。
地域信息:确认 Bucket 归属的地域,V2 的
region为必选参数。凭证配置:V1 与 V2 都从
OSS_ACCESS_KEY_ID、OSS_ACCESS_KEY_SECRET环境变量读取长期凭证,STS 场景额外读取OSS_SESSION_TOKEN。两个版本的环境变量名称一致,部署配置无需调整。若现有代码使用RAMRoleARN、ECSRAMRole、OIDCRoleARN等凭证,V2 需通过自定义凭证提供者结合credentials-java库实现,评估工作量时需计入这部分开发。调用点清单:梳理现有代码中调用 OSS SDK 的位置,记录用到的接口与功能,作为第六步的验证清单。
测试环境:准备可用于运行迁移后代码的测试环境,在发布前完成验证。
迁移路径:V1 与 V2 的包名不同,可以共存于同一项目且不会产生类冲突,因此支持两种迁移路径:
一次性整体替换(默认):适用于 OSS 调用点集中、可一次性完成回归验证的项目。第一步直接移除 V1 依赖,后续步骤覆盖项目内全部调用点。
逐模块灰度迁移:适用于调用点分散、无法一次性回归的项目。第一步暂时同时保留 V1 与 V2 两个依赖,只改写本批次迁移范围内的代码,待全部模块迁移完成后再移除 V1 依赖。
完整迁移步骤
第一步:更新 Maven 依赖
替换 pom.xml 中的依赖坐标。V1 依赖:
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.18.4</version>
</dependency>V2 依赖,<version> 替换为在 Maven Repository 查询到的最新版本号:
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibabacloud-oss-v2</artifactId>
<version><!-- 填写最新版本号 --></version>
</dependency>V1 针对 Java 9 及以上版本额外声明的 JAXB 相关依赖(javax.xml.bind:jaxb-api、javax.activation:activation、org.glassfish.jaxb:jaxb-runtime),V2 不再需要,可一并移除。
第二步:修改包名和 import
将 com.aliyun.oss 及其子包的 import 替换为 com.aliyun.sdk.service.oss2 下的对应类型。常用包的映射关系如下:
V1 包/类型 | V2 包/类型 |
|
|
|
|
|
|
|
|
|
|
| 客户端配置直接作为 |
V2 的模型类集中于 models 包,因此批量替换时建议直接引入 com.aliyun.sdk.service.oss2.models.*,再按编译错误逐个补齐缺失类型。
第三步:修改客户端创建代码
确定 region 与 endpoint
region 在所有场景下均为必选,取值为 Bucket 所在地域;endpoint 是否保留取决于访问场景。默认路径是仅设置 region、不传 endpoint,由 region 推导访问域名。
访问场景 | 是否需要 | 需要设置的开关方法 |
公网默认访问(推荐路径) | 不需要,由 | 无 |
自定义域名 | 需要,指定自定义域名 |
|
内网访问域名 | 需要,指定内网访问域名 |
|
传输加速域名 | 需要,指定传输加速域名 |
|
替换客户端创建代码
V1 需要构造凭证提供者与客户端配置对象,并显式声明 V4 签名。以下 V1 示例采用 OSSClientBuilder.create() 链式写法,使用 new OSSClientBuilder().build(endpoint, ak, sk) 的存量代码迁移后同样收敛为下面的 V2 写法。示例代码中的 <region-id> 替换为 Bucket 归属的地域,如cn-hangzhou;<endpoint> 替换为该 Bucket 的访问域名,V1 要求该参数必填。
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
String accessKeyId = System.getenv("OSS_ACCESS_KEY_ID");
String accessKeySecret = System.getenv("OSS_ACCESS_KEY_SECRET");
String region = "<region-id>";
String endpoint = "<endpoint>";
DefaultCredentialProvider provider = new DefaultCredentialProvider(accessKeyId, accessKeySecret);
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
// V1 需要显式声明使用 V4 签名算法
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.credentialsProvider(provider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.endpoint(endpoint)
.build();
// 执行 OSS 操作...
ossClient.shutdown();V2 通过静态工厂创建客户端,region 必选、默认使用 V4 签名,endpoint 仅在上表列出的场景下才需要指定。OSSClient 实现了 AutoCloseable,使用 try-with-resources 可自动释放资源:
import com.aliyun.sdk.service.oss2.OSSClient;
import com.aliyun.sdk.service.oss2.credentials.CredentialsProvider;
import com.aliyun.sdk.service.oss2.credentials.EnvironmentVariableCredentialsProvider;
CredentialsProvider provider = new EnvironmentVariableCredentialsProvider();
try (OSSClient client = OSSClient.newBuilder()
.credentialsProvider(provider)
.region("<region-id>")
.build()) {
// 执行 OSS 操作...
}凭证提供者的类型名称按第二步的映射关系替换。上面的 V2 示例使用 EnvironmentVariableCredentialsProvider 直接从环境变量读取凭证,凭证来源与 V1 示例先读取环境变量再包装为 DefaultCredentialProvider 一致;如需由代码显式传入 AccessKey,改用 StaticCredentialsProvider。
客户端的创建和销毁开销较大,高频调用场景下应复用同一实例。
V2 采用单例模式复用客户端时,不能依赖 try-with-resources,必须在应用终止前显式调用 close(),否则会造成资源泄漏。
迁移客户端配置项与重试策略
V1 通过 ClientConfiguration/ClientBuilderConfiguration 对象设置超时、重试、代理等参数;V2 将这些参数上移为 OSSClient.newBuilder() 的链式方法,如 connectTimeout、readWriteTimeout、retryMaxAttempts、retryer、proxyHost、useCName、useInternalEndpoint、useAccelerateEndpoint、disableSsl、usePathStyle。完整配置项参见 V2 SDK 文档的客户端配置章节。
重试属于可选调优项:V2 默认使用 StandardRetryer,最大尝试次数为 3 次,最大退避时间 20 秒,基础延迟 0.2 秒,采用 FullJitter 退避算法。需要调整时通过 StandardRetryer.newBuilder() 构造自定义 Retryer 并传入 retryer(...);需要关闭重试时传入 NopRetryer。
可选:改用异步客户端
如果业务需要并发执行多个互不依赖的操作,可改用异步客户端,操作返回 CompletableFuture:
import com.aliyun.sdk.service.oss2.OSSAsyncClient;
import com.aliyun.sdk.service.oss2.credentials.EnvironmentVariableCredentialsProvider;
import com.aliyun.sdk.service.oss2.models.*;
import java.util.concurrent.CompletableFuture;
try (OSSAsyncClient client = OSSAsyncClient.newBuilder()
.credentialsProvider(new EnvironmentVariableCredentialsProvider())
.region("<region-id>")
.build()) {
CompletableFuture<ListBucketsResult> future = client.listBucketsAsync(
ListBucketsRequest.newBuilder().build());
future.thenAccept(result -> {
for (BucketSummary info : result.buckets()) {
System.out.printf("bucket: name:%s, region:%s%n", info.name(), info.region());
}
});
future.join();
}第四步:改造 API 入参与出参形态
V2 将所有操作的入参收敛为 Request 对象,出参收敛为 Result 对象。本节以列举 Bucket 演示通用改造范式,其余操作按同一范式改造。
V1:
List<Bucket> buckets = ossClient.listBuckets();
for (Bucket bucket : buckets) {
System.out.println(bucket.getName());
}V2:
import com.aliyun.sdk.service.oss2.models.*;
ListBucketsResult result = client.listBuckets(
ListBucketsRequest.newBuilder().build());
for (BucketSummary info : result.buckets()) {
System.out.printf("bucket: name:%s, region:%s, storageClass:%s%n",
info.name(), info.region(), info.storageClass());
}改造时注意三点差异。一是入参:即使操作不需要任何参数,也必须通过 XxxRequest.newBuilder().build() 构造请求对象。二是出参:结果字段通过与字段同名的访问器方法读取(result.buckets()、info.name()),而非 V1 的 getXxx() getter。三是翻页:V1 需要手动维护 marker 循环拉取,V2 提供分页器接口,例如 client.listBucketsPaginator(...) 返回 ListBucketsIterable,可直接迭代所有页:
import com.aliyun.sdk.service.oss2.paginator.ListBucketsIterable;
ListBucketsIterable paginator = client.listBucketsPaginator(
ListBucketsRequest.newBuilder().build());
for (ListBucketsResult page : paginator) {
for (BucketSummary info : page.buckets()) {
System.out.println(info.name());
}
}第五步:修改异常处理逻辑
V1 需要分别捕获服务端异常与客户端异常,两者均继承自 RuntimeException:
try {
ossClient.putObject(...);
} catch (OSSException oe) {
// 请求已到达服务端但被拒绝:权限不足、参数错误、资源不存在等
System.out.println("Error Code: " + oe.getErrorCode());
System.out.println("Request ID: " + oe.getRequestId());
} catch (ClientException ce) {
// 客户端侧问题:网络不可用、请求超时、证书校验失败等
System.out.println("Error Message: " + ce.getMessage());
} finally {
ossClient.shutdown();
}V2 统一由 catch (Exception e) 捕获后,通过 ServiceException.asCause(e) 判断是否为服务端错误,是则读取 requestId()、errorCode() 等信息,否则按客户端错误处理:
import com.aliyun.sdk.service.oss2.exceptions.ServiceException;
try {
client.putObject(...);
} catch (Exception e) {
ServiceException se = ServiceException.asCause(e);
if (se != null) {
System.out.printf("ServiceException: requestId:%s, errorCode:%s%n",
se.requestId(), se.errorCode());
} else {
System.out.printf("error:%n%s", e);
}
}原有基于 OSSException.getErrorCode() 的错误码分支逻辑(如 SignatureDoesNotMatch、AccessDenied、NoSuchKey)可以直接迁移到 ServiceException.errorCode() 上,错误码取值不变。使用异步客户端时,异常由 CompletableFuture.exceptionally() 回调获取,同样通过 ServiceException.asCause(e) 提取服务端信息。
第六步:测试和验证
按下表顺序逐项验证迁移结果,每项达到通过标志后再进入下一项。
验证项 | 验证方法 | 通过标志 |
编译验证 | 编译项目,检索残留的 V1 代码形态 | 编译通过,且没有残留的 |
核心链路字段核对 | 在测试环境覆盖迁移准备阶段记录的调用点清单,重点核对上传、下载、列举、删除等核心链路的返回字段读取 | 各链路返回字段读取正确。 说明 返回类型变更是编译期无法完全暴露的风险点,必须逐项人工核对。 |
鉴权与域名验证 | 确认所有客户端都已设置 | V4 签名下的请求正常通过鉴权,域名与开关配置与第三步的场景决策表一致 |
异步路径验证 | 如果引入了异步客户端,触发正常完成与异常两条路径 |
|
灰度观察 | 灰度发布并观察错误码分布与重试次数 | 确认无异常,之后再全量切换 |
回退方案
灰度期间发现问题时,可利用两个版本可共存的特性回退:恢复 pom.xml 中的 V1 依赖,并还原本批次已改写的 import、客户端创建、API 调用与异常处理代码,回退范围与本批次改动范围一致。
相关文档
V2 版 SDK 的安装、客户端配置、访问凭证与示例代码,参见OSS Java SDK V2。
V1 版 SDK 的安装、客户端配置与异常处理说明,参见OSS Java SDK V1。