Java SDK V1到V2迁移指南

更新时间:
复制 MD 格式

OSS Java SDK V2 是对 V1(aliyun-sdk-oss)的重构版本,改造面集中在包名与 Maven 坐标、客户端创建方式和接口调用形态:客户端由静态工厂创建且必须指定地域,每个接口统一接受类型化的 Request 对象。行为上,V2 默认使用 V4 签名,并提供异步客户端。本文说明从 V1 迁移到 V2 需要处理的破坏性变更,并给出端到端的迁移流程。

变更范围评估

迁移前先按下面三类变更评估现有代码的改造范围:编译期就会暴露的阻塞项、编译能通过但行为发生变化的项、以及可选采用的新能力。V1 与 V2 的差异以本节三张表为准,其余章节只讲改造动作,不重复罗列差异。

编译期阻塞项

这些变更会直接导致编译失败,编译器可以逐个定位,改造范围相对确定。

变更项

V1

V2

对应步骤

Maven 坐标

com.aliyun.oss:aliyun-sdk-oss

com.aliyun:alibabacloud-oss-v2

第一步

JAXB 附加依赖

Java 9 及以上需手动添加

不需要

第一步

Java 版本要求

Java 7 及以上

Java 8 及以上

迁移准备

包名

com.aliyun.oss 及其子包

com.aliyun.sdk.service.oss2 及其子包,全部 import 需要重写

第二步

客户端创建

new OSSClientBuilder().build(endpoint, ak, sk)、OSSClientBuilder.create()

OSSClient.newBuilder() 静态工厂,region 与 credentialsProvider 为必选参数

第三步

客户端配置

ClientBuilderConfiguration 配置对象

Builder 链式方法

第三步

API 入参

同一操作提供多个简单参数重载

单一类型化 Request 对象(如 ListBucketsRequest),可附加 OperationOptions 覆盖单次请求的行为

第四步

API 出参

OSSObject、List<Bucket> 等,通过 getter 读取字段

GetObjectResult、ListBucketsResult 等结果对象,通过与字段同名的访问器方法读取,如 result.buckets()、info.name()

第四步

异常模型

OSSException(服务端)与 ClientException(客户端),均继承自 RuntimeException,分别捕获

捕获 Exception 后经 ServiceException.asCause(e) 判断并提取服务端错误信息

第五步

资源释放

shutdown()

实现 AutoCloseable,支持 try-with-resources

第三步

出参类型变更是唯一需要额外人工核对的一项:类型不匹配会在编译期报错,但字段是否读取正确编译期无法完全暴露,必须在第六步按核心链路逐项核对。

运行期行为变化

这些变更不会阻塞编译,但会改变请求的鉴权方式与目标域名,漏改的后果在运行期才显现。

变更项

V1

V2

对应步骤

签名算法

默认 V1 签名,使用 V4 签名需显式设置 ClientBuilderConfiguration.setSignatureVersion(SignVersion.V4) 并指定 region

signatureVersion 默认即为 v4,region 因此成为必选参数

第三步

地域与访问域名

endpoint 必填

region 必填,endpoint 可选,未显式指定时由 region 推导

第三步

可选新增能力

以下两项为非破坏性的新增能力,可按需采用,不采用不影响迁移完成。

变更项

V1

V2

对应步骤

异步能力

无独立异步客户端

OSSAsyncClient,通过 OSSAsyncClient.newBuilder() 创建,操作返回 CompletableFuture,适用于并发处理多个互不依赖的操作

第三步(可选)

列举翻页

手动维护 marker 循环拉取

分页器接口,如 client.listBucketsPaginator(...) 返回 ListBucketsIterable

第四步(可选)

迁移准备

开始改造前一次性确认以下五项,避免改到中途因环境或信息缺失返工。

  • 构建环境:确认构建与运行环境的 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 包/类型

com.aliyun.oss.OSS、com.aliyun.oss.OSSClient

com.aliyun.sdk.service.oss2.OSSClient

com.aliyun.oss.OSSClientBuilder

com.aliyun.sdk.service.oss2.OSSClientBuilder;客户端统一通过 OSSClient.newBuilder() 静态工厂构建,第三步示例中只需 import OSSClient

com.aliyun.oss.common.auth.*(如 DefaultCredentialProvider)

com.aliyun.sdk.service.oss2.credentials.*:显式传入 AccessKey 对应 StaticCredentialsProvider,从环境变量读取对应 EnvironmentVariableCredentialsProvider

com.aliyun.oss.model.*

com.aliyun.sdk.service.oss2.models.*

com.aliyun.oss.OSSException、com.aliyun.oss.ClientException

com.aliyun.sdk.service.oss2.exceptions.ServiceException

com.aliyun.oss.ClientBuilderConfiguration

客户端配置直接作为 OSSClient.newBuilder() 的链式方法,无独立配置类

V2 的模型类集中于 models 包,因此批量替换时建议直接引入 com.aliyun.sdk.service.oss2.models.*,再按编译错误逐个补齐缺失类型。

第三步:修改客户端创建代码

确定 region 与 endpoint

region 在所有场景下均为必选,取值为 Bucket 所在地域;endpoint 是否保留取决于访问场景。默认路径是仅设置 region、不传 endpoint,由 region 推导访问域名。

访问场景

是否需要 endpoint

需要设置的开关方法

公网默认访问(推荐路径)

不需要,由 region 推导

无

自定义域名

需要,指定自定义域名

useCName

内网访问域名

需要,指定内网访问域名

useInternalEndpoint

传输加速域名

需要,指定传输加速域名

useAccelerateEndpoint

替换客户端创建代码

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 代码形态

编译通过,且没有残留的 com.aliyun.oss import 与 V1 调用形式

核心链路字段核对

在测试环境覆盖迁移准备阶段记录的调用点清单,重点核对上传、下载、列举、删除等核心链路的返回字段读取

各链路返回字段读取正确。

说明

返回类型变更是编译期无法完全暴露的风险点,必须逐项人工核对。

鉴权与域名验证

确认所有客户端都已设置 region;若使用了自定义域名、内网访问域名或传输加速域名,核对对应的 endpoint 与开关参数

V4 签名下的请求正常通过鉴权,域名与开关配置与第三步的场景决策表一致

异步路径验证

如果引入了异步客户端,触发正常完成与异常两条路径

CompletableFuture 的正常完成与异常回调路径均按预期执行

灰度观察

灰度发布并观察错误码分布与重试次数

确认无异常,之后再全量切换

回退方案

灰度期间发现问题时,可利用两个版本可共存的特性回退:恢复 pom.xml 中的 V1 依赖,并还原本批次已改写的 import、客户端创建、API 调用与异常处理代码,回退范围与本批次改动范围一致。

相关文档

  • V2 版 SDK 的安装、客户端配置、访问凭证与示例代码,参见OSS Java SDK V2。

  • V1 版 SDK 的安装、客户端配置与异常处理说明,参见OSS Java SDK V1。