Skip to content

Change of behaviour in the beans loading order  #1965

Closed
@jorgerod

Description

@jorgerod

Describe the bug

Hello

I wanted to tell you about the problem I am having. The problem is unusual and will not happen easily.

The BeanFactoryPostProcessors SpringdocBeanFactoryConfigurer and SpringdocActuatorBeanFactoryConfigurer are causing a change in the loading order in an application I have with springdoc and spring-cloud-stream when there is a pollable and it is causing an error.

ConfigurableListableBeanFactory#getBeanNamesForType(Class) method is causing factorybeans to be initialised.

public static void initBeanFactoryPostProcessor(ConfigurableListableBeanFactory beanFactory) {
for (String beanName : beanFactory.getBeanNamesForType(OpenAPIService.class))
beanFactory.getBeanDefinition(beanName).setScope(SCOPE_PROTOTYPE);
for (String beanName : beanFactory.getBeanNamesForType(OpenAPI.class))
beanFactory.getBeanDefinition(beanName).setScope(SCOPE_PROTOTYPE);

Internally this call is being made:

	@Override
	public String[] getBeanNamesForType(@Nullable Class<?> type) {
		return getBeanNamesForType(type, true, true);
	}

And according to the javadoc of ListableBeanFactory, the third parameter can cause dangerous behaviour.

allowEagerInit – whether to initialize lazy-init singletons and objects created by FactoryBeans (or by factory methods with a "factory-bean" reference) for the type check. Note that FactoryBeans need to be eagerly initialized to determine their type: So be aware that passing in "true" for this flag will initialize FactoryBeans and "factory-bean" references.

In my case, BindableFunctionProxyFactory (implements FactoryBean) is being initialised early and internally doing

	protected void populateBindingTargetFactories(BeanFactory beanFactory) {
		this.bindingTargetFactories = ((ListableBeanFactory) beanFactory).getBeansOfType(BindingTargetFactory.class);
	}

. But as it is initialised early, it will not load all the beans it should. That is, the FactoryBean is created without guaranteeing that all the beans have already been created.

To Reproduce
Steps to reproduce the behavior:

  • What version of spring-boot you are using? 2.7.6
  • What modules and versions of springdoc-openapi are you using? It happens with both springdoc-webmvc and springdoc-webflux.
  • What version of springdoc you are using? 1.5.x, 1.6.x and 2.0.0
  • Occurs with springdoc.show-actuator=true and managemente.port different.

Solution

This problem can easily be solved in the following way

In SpringdocBeanFactoryConfigurer class, get beans by type with allowEagerInit flag set to false

  /**
   * Init bean factory post processor.
   *
   * @param beanFactory the bean factory
   */
  public static void initBeanFactoryPostProcessor(final ConfigurableListableBeanFactory beanFactory) {
    for (final String beanName : beanFactory.getBeanNamesForType(OpenAPIService.class, true, false)) {
      beanFactory.getBeanDefinition(beanName).setScope(SCOPE_PROTOTYPE);
    }
    for (final String beanName : beanFactory.getBeanNamesForType(OpenAPI.class, true, false)) {
      beanFactory.getBeanDefinition(beanName).setScope(SCOPE_PROTOTYPE);
    }
  }

In fact, I can make a contribution if you like.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions