You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot迁移OCI对象存储上传报错:UnsatisfiedDependencyException

迁移OCI对象存储时的HK2依赖注入错误解决

我们正尝试从AWS迁移至OCI,基于Spring Boot框架开发。此前上传文件到AWS S3完全正常,但切换到OCI对象存储后触发了如下HK2依赖注入错误:

org.glassfish.hk2.api.MultiException: A MultiException has 1 exceptions.  They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=int,parent=DaemonClientAsyncExecutorProvider,qualifiers={@javax.inject.Named("ClientAsyncThreadPoolSize")},position=0,optional=false,self=false,unqualified=null,2072183845)

    at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:51)
    at org.jvnet.hk2.internal.Utilities.justCreate(Utilities.java:1055)
    at org.jvnet.hk2.internal.ServiceLocatorImpl.create(ServiceLocatorImpl.java:968)
    at org.jvnet.hk2.internal.ServiceLocatorImpl.createAndInitialize(ServiceLocatorImpl.java:1072)
    at org.jvnet.hk2.internal.ServiceLocatorImpl.createAndInitialize(ServiceLocatorImpl.java:1064)
    at org.glassfish.jersey.inject.hk2.AbstractHk2InjectionManager.createAndInitialize(AbstractHk2InjectionManager.java:189)
    at org.glassfish.jersey.inject.hk2.ImmediateHk2InjectionManager.createAndInitialize(ImmediateHk2InjectionManager.java:30)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
    at java.base/java.util.HashMap$KeySpliterator.forEachRemaining(HashMap.java:1689)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
    at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310)
    at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
    at org.glassfish.jersey.process.internal.AbstractExecutorProvidersConfigurator.registerExecutors(AbstractExecutorProvidersConfigurator.java:65)
    at org.glassfish.jersey.client.ClientExecutorProvidersConfigurator.init(ClientExecutorProvidersConfigurator.java:139)
    at org.glassfish.jersey.client.ClientConfig$State.initRuntime(ClientConfig.java:452)
    at org.glassfish.jersey.internal.util.collection.Values$LazyValueImpl.get(Values.java:317)
    at org.glassfish.jersey.client.ClientConfig.getRuntime(ClientConfig.java:814)
    at org.glassfish.jersey.client.ClientRequest.getConfiguration(ClientRequest.java:263)
    at org.glassfish.jersey.client.JerseyInvocation.validateHttpMethodAndEntity(JerseyInvocation.java:121)
    at org.glassfish.jersey.client.JerseyInvocation.<init>(JerseyInvocation.java:90)
    at org.glassfish.jersey.client.JerseyInvocation.<init>(JerseyInvocation.java:86)
    at org.glassfish.jersey.client.JerseyInvocation.<init>(JerseyInvocation.java:77)
    at org.glassfish.jersey.client.JerseyInvocation$AsyncInvoker.method(JerseyInvocation.java:578)
    at org.glassfish.jersey.client.JerseyCompletionStageRxInvoker.method(JerseyCompletionStageRxInvoker.java:31)
    at com.oracle.bmc.http.client.jersey.JerseyHttpRequest.execute(JerseyHttpRequest.java:239)
    at com.oracle.bmc.http.internal.ClientCall.callAsyncImpl(ClientCall.java:959)
    at com.oracle.bmc.http.internal.ClientCall.callAsyncTokenRefresh(ClientCall.java:902)
    at com.oracle.bmc.retrier.BmcGenericRetrier.doFunctionCall(BmcGenericRetrier.java:175)
    at com.oracle.bmc.retrier.BmcGenericRetrier.doFunctionCallAsync(BmcGenericRetrier.java:181)
    at com.oracle.bmc.retrier.BmcGenericRetrier.lambda$executeAsync$3(BmcGenericRetrier.java:119)
    at com.oracle.bmc.waiter.GenericWaiter.executeAsync(GenericWaiter.java:109)
    at com.oracle.bmc.waiter.GenericWaiter.executeAsync(GenericWaiter.java:98)
    at com.oracle.bmc.retrier.BmcGenericRetrier.executeAsync(BmcGenericRetrier.java:112)
    at com.oracle.bmc.http.internal.ClientCall.callAsyncWithRetrier(ClientCall.java:892)
    at com.oracle.bmc.http.internal.ClientCall.callAsync0(ClientCall.java:839)
    at com.oracle.bmc.http.internal.ClientCall.callSync(ClientCall.java:1032)
    at com.oracle.bmc.objectstorage.ObjectStorageClient.putObject(ObjectStorageClient.java:1745)

当前代码实现

OsClientConfiguration配置类

@Configuration
public class OsClientConfiguration {
    // OCI配置文件路径
    String configurationFilePath = "ociconfig";
    String profile = "DEFAULT";

    public ObjectStorage getObjectStorage() throws IOException{
        // 加载OCI配置文件
        final ConfigFileReader.ConfigFile configFile = ConfigFileReader.parse(configurationFilePath, profile);
        final ConfigFileAuthenticationDetailsProvider provider =
                new ConfigFileAuthenticationDetailsProvider(configFile);

        // 构建并返回客户端
        return ObjectStorageClient.builder()
                .build(provider);
    }
}

服务类上传逻辑

@Autowired
private OsClientConfiguration configuration;

@Override
public void upload(String path,
                   String fileName,
                   Optional<Map<String, String>> optionalMetaData,
                   InputStream inputStream) throws Exception {

    PutObjectRequest putObjectRequest =
            PutObjectRequest.builder()
                    .namespaceName(namespaceName)
                    .bucketName(path)
                    .objectName(fileName)
                    .contentType(optionalMetaData.get().get("Content-Type"))
                    .contentLength(Long.valueOf(optionalMetaData.get().get("Content-Length")))
                    .putObjectBody(inputStream)
                    .build();

    try {
            configuration.getObjectStorage().putObject(putObjectRequest);
    } catch (Exception e) {
        e.printStackTrace();
        throw e;
    }finally{
        try {
            configuration.getObjectStorage().close();
            inputStream.close();
        } catch (Exception e) {
            throw new IllegalStateException("上传失败:无法关闭OCI对象存储连接", e);
        }
    }
}

依赖配置

implementation 'com.oracle.oci.sdk:oci-java-sdk-addons-resteasy-client-configurator:3.1.0'
implementation 'com.oracle.oci.sdk:oci-java-sdk-objectstorage:3.1.0'
implementation 'com.oracle.oci.sdk:oci-java-sdk-bom:3.1.0'
implementation 'com.oracle.oci.sdk:oci-java-sdk-common-httpclient-jersey:3.1.0'

解决方案

1. 修复客户端构建,显式配置HTTP客户端

错误根源是Jersey客户端的HK2容器缺少ClientAsyncThreadPoolSize参数注入,需显式构建HttpClient并配置线程池:

修改OsClientConfiguration,将客户端实例改为Spring管理的单例Bean:

@Configuration
public class OsClientConfiguration {
    String configurationFilePath = "ociconfig";
    String profile = "DEFAULT";

    @Bean
    public ObjectStorage objectStorage() throws IOException {
        ConfigFileReader.ConfigFile configFile = ConfigFileReader.parse(configurationFilePath, profile);
        ConfigFileAuthenticationDetailsProvider provider = new ConfigFileAuthenticationDetailsProvider(configFile);

        // 构建带线程池配置的HttpClient
        HttpClient httpClient = JerseyHttpClient.builder()
                .connectionTimeout(Duration.ofSeconds(10))
                .readTimeout(Duration.ofSeconds(30))
                .asyncThreadPoolSize(10) // 显式设置异步线程池大小
                .build();

        return ObjectStorageClient.builder()
                .httpClient(httpClient)
                .build(provider);
    }
}

2. 优化客户端实例管理

原代码每次上传都创建新的ObjectStorage客户端,既浪费资源又容易引发容器冲突,改为直接注入Spring管理的单例客户端:

修改服务类:

@Autowired
private ObjectStorage objectStorage; // 直接注入ObjectStorage实例

@Override
public void upload(String path,
                   String fileName,
                   Optional<Map<String, String>> optionalMetaData,
                   InputStream inputStream) throws Exception {

    PutObjectRequest putObjectRequest =
            PutObjectRequest.builder()
                    .namespaceName(namespaceName)
                    .bucketName(path)
                    .objectName(fileName)
                    .contentType(optionalMetaData.get().get("Content-Type"))
                    .contentLength(Long.valueOf(optionalMetaData.get().get("Content-Length")))
                    .putObjectBody(inputStream)
                    .build();

    try {
        objectStorage.putObject(putObjectRequest);
    } catch (Exception e) {
        throw new IllegalStateException("上传OCI对象存储失败", e);
    } finally {
        inputStream.close(); // 客户端由Spring管理,无需手动关闭
    }
}

3. 调整依赖,排除冲突

通过BOM统一管理所有OCI SDK版本,避免Jersey依赖冲突:

// 使用BOM统一管理版本
implementation platform('com.oracle.oci.sdk:oci-java-sdk-bom:3.1.0')

// 仅声明需要的模块,无需指定版本
implementation 'com.oracle.oci.sdk:oci-java-sdk-objectstorage'
implementation 'com.oracle.oci.sdk:oci-java-sdk-common-httpclient-jersey'
// 移除可能引发冲突的resteasy配置器,除非明确需要
// implementation 'com.oracle.oci.sdk:oci-java-sdk-addons-resteasy-client-configurator:3.1.0'

内容的提问来源于stack exchange,提问作者Suneet Pathak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 20:57:04