spring-projects / spring-framework

Spring Framework
https://spring.io/projects/spring-framework
Apache License 2.0
56.61k stars 38.13k forks source link

Map<String, Object> as a @Bean not working as expected even with a @Qualifier when autowired. #33537

Closed vikuwo closed 1 month ago

vikuwo commented 1 month ago

Affects: 6.1.0


Code:


import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@SpringBootApplication
public class Main implements CommandLineRunner {

  @Configuration
  public static class Config {

    @Bean("qualifiedMap")
    Map<String, Object> qualifiedMap() {
      return Map.of("k1", "v1", "k2", "v2", "k3", "v3");
    }

  }

  @Autowired
  @Qualifier("qualifiedMap")
  private Map<String, Object> qualifiedMap;

  @Autowired
  private ApplicationContext applicationContext;

  @Override
  public void run(String... args) {
    System.out.printf("Autowired Bean: %s%n", qualifiedMap);
    System.out.printf("Context Bean: %s%n", applicationContext.getBean("qualifiedMap", Map.class));
  }

  public static void main(String[] args) {
    SpringApplication.run(Main.class, args);
  }

}

Output:


Autowired Bean: {qualifiedMap={k1=v1, k3=v3, k2=v2}}
Context Bean: {k1=v1, k3=v3, k2=v2}

When we mark a Map<String, Object> as @Bean with a qualifier, @Autowired refers to a different map. The bean seems fine when directly fetched from the application context.

liua1004 commented 1 month ago

It will be fine to use @Resource, @Autowired will find candidate by type.

Map, Collection(include Set and List) and Array are different when autowiring. They will find bean which has the same type as the element they storage.

vikuwo commented 1 month ago

But this shouldn't be the case when we use @Autowire along with a @Qualifier. It should autowire the exact bean. That way, if I have the multiple Maps or Collections of the same element type, I would still be able to autowire a specific bean.

liua1004 commented 1 month ago

It depends on how to understand the annotation Qualifier. I think that it just provides a candidate, but not change the autowire logic. Maybe I am wrong.

vikuwo commented 1 month ago

At least the below statements should produce the same result. Otherwise the behaviour is not consistent.

    System.out.printf("Autowired Bean: %s%n", qualifiedMap);
    System.out.printf("Context Bean: %s%n", applicationContext.getBean("qualifiedMap", Map.class));
jhoeller commented 1 month ago

As far as I can tell, this works as of 6.2 due to #28122 - as a welcome side effect of that algorithmic optimization. Feel free to try the recently released 6.2.0-RC1 to double-check.