Library with Spring AutoConfiguration: spring.factories and what else?

Tags: , ,



We intend to develop a library with Spring Components that all of our Spring Applications should have, like Beans that connect to our monitoring, administration and ochestration services.

The idea was, to make use of Springs’ “AutoConfiguration”-mechanism.

For a Starter I wrote a class com.mycorp.project.basic.startup-notification.StartupNotificationPublisher.class which, uppon “ApplicationStartedEvent” sends a Message via our favorite message broker (which for the exmample shall be a log file), saying hello to the world.

An instance of this class shall be created through the configuration class com.mycorp.project.basic.startup-notification.autoconfiguration.StartupNotificationAutoConfiguration.class

package com.mycorp.project.basic.startup-notification
// ... Imports
@RequiredArgsConstructor
public class StartupNotificationPublisher {
    private final String topic;

    private final Logger messagePublisher = LoggerFactory.getLogger(StartupNotificationPublisher.class);

    @EventListener
    public void onApplicationStartedEvent(ApplicationStartedEvent event) {
        messagePublisher.info("{}: Hello, Attention, I am here!", topic);
    }
}

package com.mycorp.project.basic.startup-notification.autoconfiguration
// ... Imports
@Configuration
public class StartupNotificationAutoConfiguration {

    @ConditionalOnMissingBean
    @Bean
    StartupNotificationPublisher startupNotificationPublisher()
    {
        return new StartupNotificationPublisher("helloTopic");
    }
}

Finally, to make the autoconfiguration work, I placed a File spring.factories in the META-INF-Folder of the jar (yes, it is there!), containing:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=
  com.mycorp.project.basic.startup-notification.autoconfiguration.StartupNotificationAutoConfiguration

I assumed, this will make spring do its magic, find that file when I add the dependency, run the configuration and create that bean to publish the message.

This however is not what happens. Only when I add the package to the scan path of my @SpringBootApplication, I will get my message.

What did I miss? Are there more specific requirements like special project structures? Do I need to have a library called spring-boot-starter or anything like that?

ProjectStructure:

rootDirectory
|-src
|  |-main
|    |-java
|    | |-com/mycorp/project/basic/startup-notification
|    |                            |-autoconfiguration
|    |                            | |-StartupNotificationAutoConfiguration.java
|    |                            |-StartupNotificationPublisher.java
|    |-resources
|      |-META-INF
|        |-spring.factories
|-build.gradle
|-...etc...

Resulting Jar Structure:

.jar
|-META-INF
| |-spring.factories
|-com
  |-mycorp
    |-project
      |-basic
        |-startup-notification
          |-autoconfiguration
          | |-StartupNotificationAutoConfiguration.class
          |-StartupNotificationPublisher.class

Answer

Spring Factories, if configured correctly, have nothing to do with component scanning.

I can confirm that you did everything right, the same configuration worked for my projects many times. So to track what happens:

  1. Make sure that given the module “abc”, you place the spring.factories file in:

abc/src/main/resources/META-INF/spring.factories

  1. Make sure that the library jar indeed contains this file and it indeed resides in the artifact (your spring boot application). For that open the application with WinRar (or similar tool) and nagivate to BOOT-INF/lib – the jar should be there – open the jar and check that META-INF/spring.factories folder is there. If something is wrong here, check the dependencies in pom.xml the autoconfiguration module should be defined as a dependency of your spring boot application.

  2. Make sure that the configuration indeed loads (which means in turn the autoconfig module is recognized):

Rewrite the configuration in the following way:

@Configuration
public class StartupNotificationAutoConfiguration {

    public StartupNotificationAutoConfiguration() {
          System.out.println("Loading the configuration StartupNotificationAutoConfiguration"); // or use logging framework
    }

   @ConditionalOnMissingBean
   @Bean
   StartupNotificationPublisher startupNotificationPublisher()
   {
    return new StartupNotificationPublisher("helloTopic");
   }
}

If you see this message, then auto-configuration module is there and gets loaded by spring.

  1. Try to Remove @ConditionalOnMissingBean annotation. If the bean is defined in your application it will take precendence over beans defined in auto-configuration modules anyway. It can be a source of your issue as well.


Source: stackoverflow