Bug description

Summary

When using @Profile with @PostConstruct, I see the following error saying, there is a circular dependency. This error is not correct as there is no circular dependency.

Error info


The dependencies of some of the beans in the application context form a cycle:

   application (field private com.truncated.---ExtractService)
      ↓
   ----Service defined in file [/truncated/service/----Service.class]
      ↓
   s3Helper defined in file [/truncated/S3Helper.class]
┌─────┐
|  awsLocalConfig
└─────┘


S3Helper.java class uses s3Client bean.

Working spring version: 2.5.8

Failing spring version: 2.6.6

Failing code

Project contains both AwsLocalConfig.java and AwsConfig.java

Local config

Creates S3Client and sets up s3 bucket locally using @PostConstruct


@Configuration
@Slf4j
@Profile("local")
public class AwsLocalConfig {

    private static final String LOCALSTACK_URL = "http://127.0.0.1:4566";

    @Value("${config.aws.region}")
    String region;

    @Value("${config.aws.access_key}")
    String awsS3AccessKey;

    @Value("${config.aws.secret_key}")
    String awsS3SecretKey;

    @Value("${config.aws.s3bucket}")
    String s3BucketName;


    @Bean
    @Qualifier("s3Client")
    public AmazonS3 s3Client() {
        return s3ClientBasicCredentials();
    }

    @Bean
    @Qualifier("s3ClientBasicCredentials")
    public AmazonS3 s3ClientBasicCredentials() {
        AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(
                new BasicAWSCredentials(awsS3AccessKey, awsS3SecretKey));
        EndpointConfiguration endpointConfiguration = new EndpointConfiguration(LOCALSTACK_URL, region);
        return AmazonS3ClientBuilder.standard()
                .withCredentials(credentialsProvider)
                .withEndpointConfiguration(endpointConfiguration)
                .build();
    }


    @PostConstruct
    public void prepareLocalStackResources() {
        AmazonS3 amazonS3 = s3ClientBasicCredentials();
        amazonS3.createBucket(s3BucketName);
        log.info(String.format("S3 Bucket %s created successfully in localstack", s3BucketName));
    }

}

Non local config (dev/qa/stg/prod)

Creates S3Client bean and project starts up without issues as there is no usage of @PostConstruct.

Works even in 2.6.6 version

@Configuration
@Slf4j
@Profile("!local")
public class AwsConfig {

    @Value("${config.aws.region}")
    String region;

    @Value("${config.aws.access_key}")
    String awsS3AccessKey;

    @Value("${config.aws.secret_key}")
    String awsS3SecretKey;


    @Bean
    @Qualifier("s3Client")
    public AmazonS3 s3Client() {

        log.info("AWS_ROLE_ARN env variable value is: " + System.getenv("AWS_ROLE_ARN"));
        log.info("AWS_ROLE_SESSION_NAME env variable value is: " + System.getenv("AWS_ROLE_SESSION_NAME"));

        AmazonS3 amazonS3 = AmazonS3ClientBuilder.standard()
                .withCredentials(WebIdentityTokenCredentialsProvider.create())
                .withRegion(region)
                .build();

        log.info("AWS S3 Account owner display name is :" + amazonS3.getS3AccountOwner().getDisplayName());
        log.info("AWS S3 Account owner id is:" + amazonS3.getS3AccountOwner().getId());

        return amazonS3;
    }

    @Bean
    @Qualifier("s3ClientBasicCredentials")
    public AmazonS3 s3ClientBasicCredentials() {
        AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(
                new BasicAWSCredentials(awsS3AccessKey, awsS3SecretKey));
        return AmazonS3ClientBuilder.standard()
                .withRegion(region)
                .withCredentials(credentialsProvider)
                .build();
    }

}

Temporary Solution

Not using @PostConstruct and calling prepareLocalStackResources() inside s3ClientBasicCredentials() bean creation method.

Works without issues in 2.6.6 version


@Configuration
@Slf4j
@Profile("local")
public class AwsLocalConfig {

    private static final String LOCALSTACK_URL = "http://127.0.0.1:4566";

    @Value("${config.aws.region}")
    String region;

    @Value("${config.aws.access_key}")
    String awsS3AccessKey;

    @Value("${config.aws.secret_key}")
    String awsS3SecretKey;

    @Value("${config.aws.s3bucket}")
    String s3BucketName;


    @Bean
    @Qualifier("s3Client")
    public AmazonS3 s3Client() {
        return s3ClientBasicCredentials();
    }

    @Bean
    @Qualifier("s3ClientBasicCredentials")
    public AmazonS3 s3ClientBasicCredentials() {
        AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(
                new BasicAWSCredentials(awsS3AccessKey, awsS3SecretKey));
        EndpointConfiguration endpointConfiguration = new EndpointConfiguration(LOCALSTACK_URL, region);
        AmazonS3 s3 = AmazonS3ClientBuilder.standard()
                .withCredentials(credentialsProvider)
                .withEndpointConfiguration(endpointConfiguration)
                .build();
        prepareLocalStackResources(s3);
        return s3;
    }


    public void prepareLocalStackResources(AmazonS3 amazonS3) {
        amazonS3.createBucket(s3BucketName);
        log.info(String.format("S3 Bucket %s created successfully in localstack", s3BucketName));
    }

}

Comment From: neerajsu

Seems like this issue with already raised in spring-framework here -> https://github.com/spring-projects/spring-framework/issues/27876. So closing out, since this is a duplicate.