Spring Boot (2.1.9) Spring Beans (5.1.10) Java 12
I want to discuss issue that is related to registerBean() and getIfAvailable() methods.
Short version
objectProvider.getIfAvailable();
applicationContext.registerBean(String name, Class classType)
applicationContext.getBean(Class) -> no such bean definition exception
applicationContext.registerBean(String name, Class classType)
applicationContext.getBean(Class) -> returns bean
Long version Let us take a look at the example:
private GenericApplicationContext applicationContext;
public <T> T getOrCreateBean(Class<T> classType, String name) {
T reader;
ObjectProvider<T> readerProvider = applicationContext.getBeanProvider(classType);
if (Objects.isNull(readerProvider.getIfAvailable())) {
applicationContext.registerBean(name, classType);
reader = applicationContext.getBean(name, classType);
} else {
reader = readerProvider.getIfAvailable();
}
return reader;
}
General idea is to find already registered bean or create a new one if it does not exist.
The problem is that ObjectProvider#getIfAvailable eventually calls this method: org.springframework.beans.factory.support.DefaultListableBeanFactory#getBeanNamesForType(java.lang.Class<?>, boolean, boolean)
which will put this class into cache
Map<Class<?>, String[]> cache =
(includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
// ...
if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
cache.put(type, resolvedBeanNames);
}
Later when I want this T class that I registered inject in constructor in my case:
public Writer (MyRegisteredService myRegisteredServiceBeanName) {
this.service = myRegisteredServiceBeanName;
}
I will receive NoSuchBeanDefinitionException despite it is registered!
That is because org.springframework.beans.factory.support.DefaultListableBeanFactory#allBeanNamesByType does not contain bean name or in another words: empty String[].
genericApplicationContext.getBean(String name) will successfully return that bean that could not be autowired by class.
This code works pretty well by the way:
public <T> T createBean(Class<T> classType, String name) {
applicationContext.registerBean(name, classType);
return applicationContext.getBean(name, classType);
}
Is such behavior expected?
Comment From: mondaka
A similar bug is happening in my app when I try to use getBean(Class
Debugging, I can see that the problem is the same. The bean is found in "allBeanNamesByType" but it returns an empty String, which is causing an NotSuchBeanDefinitionException.
Comment From: jhoeller
This looks like a duplicate of #24852 which got fixed in 5.2.6 and also backported to 5.1.15. If a variant the problem still exists, feel free to reopen this issue.
Comment From: hassibnkhoury
so what is the solution? i am using the latest version of spring boot 3.2.1 and still the issue of memory appears: One instance of “org.springframework.beans.factory.support.DefaultListableBeanFactory” loaded by “org.springframework.boot.loader.launch.LaunchedClassLoader @ 0xf006e300” occupies 1,025,792 (11.57%) bytes. The memory is accumulated in one instance of “org.springframework.beans.factory.support.DefaultListableBeanFactory”, loaded by “org.springframework.boot.loader.launch.LaunchedClassLoader @ 0xf006e300”, which occupies 1,025,792 (11.57%) bytes.
Keywords org.springframework.beans.factory.support.DefaultListableBeanFactory org.springframework.boot.loader.launch.LaunchedClassLoader