Bug Report Summary:

Medium sized application using spring-boot and spring-cloud-streams, running into consistency issues when updating version of spring boot.

I've included the relevant spring-cloud-stream code, but I do not believe it is related to spring cloud stream, as the error is at the bean resolution level on startup.

Problem:

When using Spring Boot 3.1.4, there are 4 beans with unique names that are successfully injected via constructor injection into a different class. However, updating to Spring Boot 3.2.0-M1 (and no other changes) results in the following error: Parameter 0 of constructor in com.StreamConfiguration required a single bean, but 4 were found

I attempted to re-create the issue in a small sample project but was unable to get the issue to re-occur. But it happens reliably in the larger, internal project. This project has been working reliably since Spring Boot 2.6 and has been upgraded at a regular cadence for roughly three years.

The project is using: OpenJDK Runtime Environment GraalVM CE 21+35.1 (build 21+35-jvmci-23.1-b15) and

plugins {
//    id 'org.springframework.boot' version '3.2.0-M1'
    id 'org.springframework.boot' version '3.1.4'
    id 'io.spring.dependency-management' version '1.1.0'
...
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}
...
sourceCompatibility = '21'

The relevant code:

@Configuration
@AllArgsConstructor
public class StreamConfiguration
{
    private final Predicate<String, Event> validMessagePredicate;
    private final Predicate<String, Event> createdPredicate;
    private final CreatedConsumer createdConsumer;
    private final Predicate<String, Event> updatedPredicate;
    private final UpdatedConsumer updatedConsumer;
    private final Predicate<String, Event> deletedPredicate;
    private final DeletedConsumer deletedConsumer;
    private final DefaultConsumer defaultConsumer;

    @Bean
    public Consumer<KStream<String, Event>> processNotification() {
        return input -> input
            .filter(validMessagePredicate)
            .split()
            .branch(createdPredicate, Branched.withConsumer(createdConsumer))
            .branch(updatedPredicate, Branched.withConsumer(updatedConsumer))
            .branch(deletedPredicate, Branched.withConsumer(deletedConsumer))
            .defaultBranch(Branched.withConsumer(defaultConsumer));

    }
}

and the configuration class

@Configuration
public class PredicateConfiguration
{
    private static final String PREFIX = "test";
    private static final String CREATED_EVENT = "created";
    private static final String UPDATED_EVENT = "updated";
    private static final String DELETED_EVENT = "deleted";

    @Bean
    public Predicate<String, Event> validMessagePredicate() {
        return (k, v) -> !StringUtils.isEmpty(v.getEventName()) &&
            v.getEventName().contains(PREFIX);
    }

    @Bean
    public Predicate<String, Event> createdPredicate() {
        return (k, v) -> CREATED_EVENT.equals(v.getEventName());
    }

    @Bean
    public Predicate<String, Event> updatedPredicate() {
        return (k, v) -> UPDATED_EVENT.equals(v.getEventName());
    }

    @Bean
    public Predicate<String, Event> deletedPredicate() {
        return (k, v) -> DELETED_EVENT.equals(v.getEventName());
    }
}

When the version is set in gradle to id 'org.springframework.boot' version '3.1.4', the application starts up fine, and all the beans are loaded by name into the correct workflows.

Updating to id 'org.springframework.boot' version '3.2.0-M1' results in the error mentioned above (full error):

Parameter 0 of constructor in com.StreamConfiguration required a single bean, but 4 were found: - validMessagePredicate: defined by method 'validMessagePredicate' in class path resource [com/.../PredicateConfiguration.class] - createdPredicate: defined by method 'createdPredicate' in class path resource [com/.../PredicateConfiguration.class] - updatedPredicate: defined by method 'updatedPredicate' in class path resource [com/.../PredicateConfiguration.class] - deletedPredicate: defined by method 'deletedPredicate' in class path resource [com/.../PredicateConfiguration.class]

No changes are made except the spring boot version update.

This occurs on 3.2.0-M1,M2 and M3

Comment From: wilkinsona

Dependency injection is performed by Spring Framework so I think this is out of Spring Boot's control. I suspect the change is related in some way to Spring Boot upgrading to Framework 6.1. I don't recall any changes in this area and I don't see anything in the upgrade guide either. We'll transfer this to the Framework team so that they can investigate.

Comment From: snicoll

I suspect you're not compiling your code with -parameters as you're relying on the bean name, see this section of the upgrade guide.

I think you should also have seen a warning in the logs with the previous version, something along the lines of

Using deprecated '-debug' fallback for parameter name resolution. Compile the affected code with '-parameters' instead or avoid its introspection: StreamConfiguration

Comment From: StevenPG

@snicoll just verified, you are correct. A great deal of configuration classes are outputting the deprecated '-debug' message

Is the intent to no longer allow method names and instead "require" @Qualifier at all injection points unless the -parameters flag is passed?

I'm currently wrestling with Gradle to try and verify -parameters resolves the issue.

I want to make sure I'm taking the correct approach.

Comment From: snicoll

Is the intent to no longer allow method names and instead "require" @qualifier at all injection points unless the -parameters flag is passed?

We just requires parameter names to be retained in the bytecode is all. It's long overdue and the deprecated code has been removed in 6.1 which is why you're seeing this.

Can you please confirm you saw a warning in the logs with Spring Boot 3.1.x? I want to make sure that this was covered.

Comment From: StevenPG

Confirmed: Using deprecated '-debug' fallback for parameter name resolution. Compile the affected code with '-parameters' instead or avoid its introspection: com...StreamConfiguration

Got it working on my end configuring -parameters in the build and the IDE. Thank you for clarifying!

Comment From: sbrannen

Thanks for the feedback, @StevenPG.

I'm closing this issue as a result.