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 type) (Spring Version 4.3.18). This method uses getBeanNamesForType from DefaultListableBeanFactory:

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