I don't understand how it is possible to see exception messages like this one
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.util.List<java.lang.String>' available
Aren't type arguments supposed to be erased by runtime? How can things like <java.lang.String>
survive compilation (as we know, exceptions occur during runtime)? What kind of supernatural ability does Spring possess?
This question is similar, but none of the two most-upvoted answers answer my question exactly
As discussed in a answer to Stack Overflow question Why is this generic type information for member field not erased in Java?, generic type information of fields is reflectively available in Java. This is also true of method/constructor parameters. This explains how Spring can know that a particular generic type is required.
Additionally, bean definitions are often done via concrete classes or bean methods. Both of these cases retain their type information at compile time. This explains how Spring can know what the specific generic type of a bean is.
Putting these two together explains how Spring is able to fail when no bean matching a specific generic signature exists.
To make it concrete, I'm going to give an example. Let's say we have the following generic class:
public class GenericBean<T> {
}
Here are two bean definitions, one which is defined as a bean by using the @Service
annotation on a subclass, and one by using @Bean
within a Configuration
class:
@Service
public class GenericBeanService extends GenericBean<Integer> {
}
@Configuration
public class GenericBeanConfig {
@Bean
public GenericBean<String> genericBean() {
return new GenericBean<>();
}
}
In both of these cases, the generic type information of these beans is available at runtime using reflection. This means that Spring can use reflection to determine the specific generic types of the beans:
// GenericBean<String>
GenericBeanConfig.class.getMethod("genericBean").getGenericReturnType();
// GenericBean<Integer>
GenericBeanService.class.getGenericSuperclass();
Here is an autowired class that uses the generic beans:
@Service
public class AutowiredClass {
@Autowired private GenericBean<String> stringBean;
@Autowired private GenericBean<Integer> integerBean;
}
Here too, the generic type information of the autowired fields is available at runtime using reflection. This means that Spring can use reflection to determine the specific generic types of the beans:
// GenericBean<String>
AutowiredClass.class.getDeclaredField("stringBean").getGenericType()
// GenericBean<Integer>
AutowiredClass.class.getDeclaredField("integerBean").getGenericType()
Since Spring can determine via reflection the generic types of the beans, and the types of the autowired properties, it can therefore correctly assign beans based on their generics.