@Configuration
public class Config {

  @Bean
  Object mapConfigDemo1(@Qualifier("sampleMap1") HashMap<String, Object> hashMap) {
    System.out.println("map1: "+hashMap.entrySet().stream().map(Objects::toString).collect(Collectors.joining(",")));
    return hashMap.get("key");
  }

  @Bean
  HashMap<String, Object> sampleMap1() {
    return new HashMap<>() {
      {
        put("key", "1");
      }
    };
  }

  @Bean
  Object mapConfigDemo2(@Qualifier("sampleMap2") Map<String, String> hashMap) {
    System.out.println("map2: "+hashMap.entrySet().stream().map(Objects::toString).collect(Collectors.joining(",")));
    return hashMap.get("key");
  }

  @Bean
  Map<String, String> sampleMap2() {
    return new HashMap<>() {
      {
        put("key", "2");
      }
    };
  }


  @Bean
  Object mapConfigDemo3(@Qualifier("sampleMap3") Map<String, Object> hashMap) {
    System.out.println("map3: "+hashMap.entrySet().stream().map(Objects::toString).collect(Collectors.joining(",")));
    return hashMap.get("key");
  }

  @Bean
  Map<String, Object> sampleMap3() {
    return new HashMap<>() {
      {
        put("key", "3");
      }
    };
  }

}

I would have expected that the console output would be:

map1: key=1

map2: key=2

map3: key=3

Actually the console output is:

map1: key=1

map2: key=2

map3: sampleMap3={key=3}

Comment From: sbrannen

I've edited your comment to improve the formatting. You might want to check out this Mastering Markdown guide for future reference.

Comment From: snicoll

In general, you should not expose beans of type Map or List as the core container has special handling for this types (gathering a mapping from bean name to bean, and collection the beans of a certain type).

@Qualifier doesn't act as bean-name semantic as you seem to be using it. It really is a by-type injection with an additional hint in case of multiple by-type candidates. And the Map<String,?> is the framework really trying to do the best it can with what you're asking. The target will be mapped to another map of bean name to type.

I can see how that's surprising, but there's nothing we can do without breaking something else. Use dedicated types to wrap beans like that.