【问题标题】:Error creating bean with name 'amazonS3Client': Requested bean is currently in creation: Is there an unresolvable circular reference?创建名为“amazonS3Client”的 bean 时出错:当前正在创建请求的 bean:是否存在无法解析的循环引用?
【发布时间】:2020-03-24 15:01:16
【问题描述】:

一切都开始了,因为 Spring Cloud AWS 没有正确配置 SimpleStorageProtocolResolver。此类负责在使用 ResourceLoader 时处理 s3:// 协议。有关详细信息,请参阅问题:cannot be cast to org.springframework.core.io.WritableResource on Spring AWS example

所以,我不得不手动创建它。但我也在使用 LocalStack 解决方案 (https://github.com/localstack/localstack),并且由于 Spring Cloud AWS 没有手动配置端点的选项,我不得不(猜猜是什么?)通过创建 AmazonS3Client手。

问题来了,当我在我的类 S3Configuration 类(见下文)中创建两个 bean 时,Spring Framework 只是跳过了 bean 创建。而且,当我尝试将其连接到 S3Handler 类(见下文)时,会发生此错误:Error Creating a bean with name 'amazonS3Client': Requested bean当前正在创建:是否存在无法解析的循环引用?

但是,这里又来了整件事,这些类之间没有循环引用。

我已经简化了项目,所以我可以在这里发布:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
        <relativePath/>
    </parent>
    <groupId>io.mobi7.ms</groupId>
    <artifactId>mobi7-temp-ms-ibutton-worker</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <properties>
        <spring-cloud-version>2.1.4.RELEASE</spring-cloud-version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-aws</artifactId>
            <version>${spring-cloud-version}</version>
        </dependency>
    </dependencies>

</project>

S3Configuration.java

package io.mobi7.ms.ibutton.worker;

import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.aws.core.io.s3.SimpleStorageProtocolResolver;
import org.springframework.cloud.aws.core.region.RegionProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.DefaultResourceLoader;

@Configuration
public class S3Configuration {

    @Bean("amazonS3Client") // THIS METHOD IS NOT BEING CALLED!!!
    public AmazonS3 amazonS3Client(AWSCredentialsProvider credentialsProvider,
                                   RegionProvider regionProvider,
                                   @Value("${cloud.aws.s3.default-endpoint}") String endpoint) {
        return AmazonS3ClientBuilder.standard()
            .withCredentials(credentialsProvider)
            .withEndpointConfiguration(
                new AwsClientBuilder.EndpointConfiguration(endpoint, regionProvider.getRegion().getName()))
            .build();
    }

    @Autowired  // THIS METHOD IS NOT BEING CALLED!!!
    public void configureResourceLoader(@Qualifier("amazonS3Client") AmazonS3 amazonS3Client,
                                        DefaultResourceLoader resourceLoader) {
        SimpleStorageProtocolResolver simpleStorageProtocolResolver = new SimpleStorageProtocolResolver(amazonS3Client);
        // As we are calling it by hand, we must initialize it properly.
        simpleStorageProtocolResolver.afterPropertiesSet();
        resourceLoader.addProtocolResolver(simpleStorageProtocolResolver);
    }
}

S3Handle.java

package io.mobi7.ms.ibutton.worker;

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.stream.Collectors;

@Component
public class S3Handler {

    private AmazonS3 amazonS3Client;

    @Autowired
    public S3Handler(@Qualifier("amazonS3Client") AmazonS3 amazonS3Client) {
        this.amazonS3Client = amazonS3Client;
    }

    public List<String> listFiles(String bucketName) {
        return amazonS3Client.listObjects(bucketName).getObjectSummaries().stream().map(S3ObjectSummary::getKey)
            .collect(Collectors.toList());
    }
}

Application.java

package io.mobi7.ms.ibutton.worker;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class).web(WebApplicationType.NONE).run(args);
    }

    @Autowired
    public void listFiles(S3Handler s3Handler) {
        s3Handler.listFiles("default-s3-bucket").forEach(System.out::println);
    }
}

application.properties

cloud.aws.region.static=us-east-1
cloud.aws.stack.auto=false
cloud.aws.s3.default-endpoint=http://localhost:4572

任何想法,为什么会发生这种情况?

【问题讨论】:

  • 问题的部分原因是您正在做所有配置时间。我建议创建一个返回ApplicationRunner@Bean 方法而不是您的Application.listFiles 方法来执行相同的操作。这应该会延迟创建并给予适当的时间来构造对象。
  • 这是有道理的,但是创建 listFiles 方法只是为了简化这里的问题,即使我在@RestController 中插入 S3Handler,也会发生同样的错误。

标签: spring spring-boot spring-cloud-aws


【解决方案1】:

@Autowired 注解标记了构造对象状态的方法(例如 setter 或方法注入)。请参阅“自动装配方法”部分 here

您使用 Autowired 注释的两种方法都不能满足此需求。

这个问题是关于春天的。所有其他标签都可以删除。


更新:here 涵盖了类似情况。
无论那里建议什么,我都不会保留一个 GOD 配置类,而是将您的配置类拆分为 2 个类 - S3 客户端的工厂和资源加载器的配置。

【讨论】:

  • 我不同意,类 S3Configuration 中的方法 configureResourceLoader 是一个配置方法。我需要重新配置 DefaultResourceLoader。在您传递的文档中,说我可以在配置方法中做到这一点:“配置方法可能有任意名称和任意数量的参数;这些参数中的每一个都将与 Spring 容器中的匹配 bean 自动装配” .
  • 第二个@autowired 方法创建它只是为了简化解决方案。 Spring 应该按以下顺序创建/自动装配:S3Configuration.amazonS3Client、S3Configuration.configureResourceLoader、S3Handler.S3handler、Application.listFiles。我不知道为什么,但如果从 configureResourceLoader 方法中删除限定符,它会完美运行。这根本没有意义。
  • @FelipeDesiderati 这里有介绍stackoverflow.com/a/28748366/1262265我更新了答案
  • Thaaaaaaaaanks,现在更有意义了。
猜你喜欢
  • 2021-11-21
  • 2017-10-01
  • 1970-01-01
  • 2017-05-29
  • 1970-01-01
  • 2018-09-05
  • 2021-05-15
  • 1970-01-01
  • 2015-06-29
相关资源
最近更新 更多