Hello, We have a shared commons library with @AutoConfiguration that was using spring.factories. After upgrade to Spring Boot 3.0.9 (from 2.7.x) our applications tests were failing due to:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.company.commons.props.AppHttpClientsProps' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

I might add that com.company.commons.props.AppHttpClientsProps is within the commons library.

We then migrated to org.springframework.boot.autoconfigure.AutoConfiguration.imports and problem has not been seen since. https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide#auto-configuration-files

Applications include commons library as a dependency.

package com.company.commons;

import com.company.commons.config.*;
import com.company.commons.spring.YamlPropertySourceFactory;
import org.springframework.boot.autoconfigure.AutoConfigurationPackage;
import org.springframework.context.annotation.*;

@ComponentScan(
    basePackages = "com.company.commons",
    excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Configuration.class)
)
@Import({
    CoreDefaultConfig.class,
    SecurityConfig.class
    /* omitted other configurations */
})
@PropertySource(value = "classpath:commons.yaml", factory = YamlPropertySourceFactory.class)
@AutoConfigurationPackage
public class CommonsAutoConfiguration {
}

package com.company.commons.config;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration
@EnableScheduling
@EnableConfigurationProperties
public class CoreDefaultConfig {
}

package com.company.commons.props;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Getter
@Setter
@Component
@ConfigurationProperties("app.http-clients")
public class AppHttpClientsProps {

  private HttpClient example;

  @Getter
  @Setter
  public static class HttpClient {

    private String url;
    private String username;
    private String password;
  }
}

spring.factories:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.company.commons.CommonsAutoConfiguration

Application configuration is:

package com.company.myapplication;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication {

  public static void main(final String[] args) {
    SpringApplication.run(MyApplication.class, args);
  }
}

Comment From: scottfrederick

Thanks for the code snippets, but trying to assemble snippets like this into a running application that we can test ourselves is difficult. What you have provided so far is missing references like YamlPropertySourceFactory and SecurityConfig that might be important.

If you would like us to spend some time investigating, please provide a complete minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it and attaching it to this issue.

Comment From: spring-projects-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: SpiReCZ

Ok, working on it. Should be ready within an hour or so.

Comment From: SpiReCZ

Repository with minimal project having the issue: https://github.com/SpiReCZ/spring-di-factories-compat/ CI errors log about the issue: https://github.com/SpiReCZ/spring-di-factories-compat/actions/runs/7208534486/job/19637675715

Comment From: bclozel

Hello @SpiReCZ , thanks for the repro project.

As you've pointed out in the migration guide, auto-configuration class entries in spring.factories are not supported anymore as of Spring Boot 3.0. You've found out that adding the auto-configuration name in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports indeed solves the issue.

With that, I'm not sure if there's anything else missing here. Are you expecting your library to work with both 2.7 and 3.x? It's technically possible for this specific case by listing your auto-configurations in both spring.factories and org.springframework.boot.autoconfigure.AutoConfiguration.imports. Spring Boot 2.7.x supports both and will de-duplicate. Spring Boot 3.x will only look at org.springframework.boot.autoconfigure.AutoConfiguration.imports. Note that there are many other questions about supporting both 2.7 and 3.x with a single librarry. There are many requirement changes and features that would make this difficult. I would think that releasing another major version of that library for the occasion would be a good fit.

While reviewing this sample I noticed a few unusual things about the setup there. For example, scanning with @ComponentScan from an auto-configuration is highly discouraged. I think a typical Spring Boot auto-configuration would look like:

// @Conditional* annotations here
@AutoConfiguration
@EnableConfigurationProperties(AppHttpClientsProps.class)
public class CommonsAutoConfiguration {

  // @Bean methods here
}

and its compation properties class only needs

@ConfigurationProperties("app.http-clients")
public class AppHttpClientsProps {
//...
}

We've tried to document those best practices in our reference documentation, hopefully this can be helpful.

I'm closing this issue because this all works as designed.

Comment From: SpiReCZ

@bclozel Thx, I have a looked again at the migration guide and it indeed says the support is dropped. I must have got stuck on the compat support from 2.7 not realizing the 3.0 compat removal.

We try to provide common functionality for microservices that reside in the same repository. If we were creating separate starters for every functionality and not knowing who will integrate it, I guess it would be apropriate to write them with best practices like mentioned. This commons library is more like an application without any business logic having just the setup there, so developers can focus on the features in the applications. It also shares some of the reused DTOs. Not sure if that case is still valid for@ComponentScan removal, but it seems this way of reusing components/logic/setup is not encouraged.

Comment From: bclozel

Not sure if that case is still valid for @ComponentScan removal, but it seems this way of reusing components/logic/setup is not encouraged.

This is a general recommendation that we crafted with our own experience with the community. We've learned that developers often want conditional registration of beans and a way to override them with their own opinions. Also, some apps scan packages too widely and this can lead to double registration and issues.

If this setup suits your needs and you've never had this type of problem, it's up to you to choose the best approach.