Costin Leau opened SPR-7915 and commented
When injecting an entity that extends Map, the container automatically assumes the bean names/instances need to be injected even if that's not the case. Example and discussion here: https://jira.springsource.org/browse/SGF-22
Affects: 3.0.5
Attachments: - spring-autowiring-testproject.zip (2.65 kB)
Issue Links:
- #15894 Make spring support like CDI @Produces
(for Array/Map/Collection inject) ("is duplicated by")
- #18536 Optional autowire of Map@Autowired
does not work for target bean of type Collection
- #18904 Doc: Constructor injection of arrays and collections
- #18162 Cannot inject List@Named
Referenced from: commits https://github.com/spring-projects/spring-framework/commit/4a0fa69ce469cae2e8c8a1a45f0b43f74a74481d
2 votes, 3 watchers
Comment From: spring-projects-issues
Olaf Otto commented
I am also affected by this issue. Types compatible to Collection / Map lead to exceptions during autowiring.
The origin (as of 3.0.6.release) is in the DefaultListableBeanFactorie's method
doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName, Set\
(beanName being the name of the bean who's members are being autowired, type the Class of the field that is autowired)
// -- snipp
else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
Class elementType = descriptor.getCollectionType();
if (elementType == null) {
if (descriptor.isRequired()) {
throw new FatalBeanException("No element type declared for collection [" + type.getName() + "]");
}
return null;
}
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType, descriptor);
if (matchingBeans.isEmpty()) {
if (descriptor.isRequired()) {
raiseNoSuchBeanDefinitionException(elementType, "collection of " + elementType.getName(), descriptor);
}
return null;
}
if (autowiredBeanNames != null) {
autowiredBeanNames.addAll(matchingBeans.keySet());
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
return converter.convertIfNecessary(matchingBeans.values(), type);
}
else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
Class keyType = descriptor.getMapKeyType();
if (keyType == null || !String.class.isAssignableFrom(keyType)) {
if (descriptor.isRequired()) {
throw new FatalBeanException("Key type [" + keyType + "] of map [" + type.getName() +
"] must be assignable to [java.lang.String]");
}
return null;
}
Class valueType = descriptor.getMapValueType();
if (valueType == null) {
if (descriptor.isRequired()) {
throw new FatalBeanException("No value type declared for map [" + type.getName() + "]");
}
return null;
}
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType, descriptor);
if (matchingBeans.isEmpty()) {
if (descriptor.isRequired()) {
raiseNoSuchBeanDefinitionException(valueType, "map with value type " + valueType.getName(), descriptor);
}
return null;
}
if (autowiredBeanNames != null) {
autowiredBeanNames.addAll(matchingBeans.keySet());
}
return matchingBeans;
}
else {
I think there are two issues with this:
- IMO wer're checking for a Bean here that is compatible to the field type that is to be autowired, i.e. it must be
type.isAssignableFrom(Collection.class);
and
type.isAssignableFrom(Map.class);
and not the other way around
- I think this should not be implicit behavior nut rather explicit. It's rather obvious one can get into a use case where this type of autowiring is not desirable but cannot be suppressed by other means than changing the field type to something that is not a collection.
In general, I think this is a rather esoteric use case...
Comment From: spring-projects-issues
Olaf Otto commented
Attached a maven project verifying the bug against 3.0.6.RELEASE
Comment From: spring-projects-issues
Chris Beams commented
This deserves more careful attention and testing than we can afford given the close proximity of 3.1 GA; slating for 3.2 Backlog.
In the meantime, for the use cases listed, switching to @Resource(name="beanName")
should achieve the desired effect.
I do agree, however, that in cases such as @Autowired AssignableToMap foo;
that the container should first beans matching the declared type before falling back to current @Autowired Map
semantics, which is to to populate the map with beanName/beanInstance pairs.
Also note that I've added the reproduction case attached here to the spring-framework-issues project. See https://github.com/SpringSource/spring-framework-issues/commit/f398411c0490322bc80c42dd7d8b77c853f3b721
Comment From: sbrannen
If you have shown interest in this issue, you may also be interested in the following which is currently scheduled for inclusion in Spring Framework 6.1.
-
30022