Auto-configure your common module in the “Spring-Boot-Way”

Noah Hsu
JavaToDev
Published in
4 min readMar 26, 2023
copy images from IGHT-IMMA-HEAD-OUT-MEME and Spring Boot Logo

Building a common module for other modules, projects, or even teams to use is happening repeatedly during our software engineer job. So, how to make it easy to use, and even as a plug-and-work plugin is one of the key points to promote it.

In this article, we will introduce the Spring-Boot way of how to auto-configure the Spring Bean and make it work in the project that needs the component in our common module. In other words, we will write the configuration in our common module ( as an external jar) with a totally different package name and still be ComponentScan by the Spring Boot mechanism.

How to do

The setting is very easy, assume we have some Component (i.e. filter, aspect, etc.) that we want to provide to other modules/projects to use in the common module.

Setup Common module

There are some steps to do so:

  1. A Configuration class to provide those beans.
  2. @ConditionalOnProperty on the class level.
    Why: make users can disable the feature by property in a test or other scenarios.
    - prefix: the prefix of the target property
    - name: the target property name
    - havingValue: the value of the target property name that means enable
    - matchIfMissing: enable/disable when didn’t define the target property
  3. @ConditionalOnMissingBean on the bean level.
    Why: make user can build their own bean to override the default bean provided by the common module
    - type: build if a bean belongs to the same class doesn’t exist
    - name: build if a bean with the same name doesn’t exist
  4. Add a file in META-INF to make Spring Boot scan the Configuration class.

Let's see how would the code look like:

package org.example.config

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "some.prefix", name = "target", havingValue = "true", matchIfMissing = true)
public class XXXConfiguration {

@Bean
@ConditionalOnMissingBean(SomeComponent1.class)
// or @ConditionalOnMissingBean(type = "SomeComponent1")
SomeComponent1 someComponent1() {
return new DefaultSomeComponent1();
}

@Bean("someComponent2")
@ConditionalOnMissingBean(name = "someComponent2")
SomeComponent2 someComponent2() {
return new DefaultSomeComponent2();
}

}

and then add a file named org.springframework.boot.autoconfigure.AutoConfiguration.imports under the folder: main/resources/META-INF/spring/ in the common module.

org.example.config.XXXConfiguration

(available after Spring Boot 2.7.0, and Spring Boot 3)

if the version of Spring Boot < 2.7.0, then we should provide a different file named spring.factories in a different path in main/resources/META-INF/ and content like below:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.example.config.ObserveConfiguration

(still working in Spring Boot 2.7.x, but deleted after Spring Boot 3)

User module

After this setting, we can add the dependency and then the spring boot will build those spring components automatically and make them work in our Application.

if we want to disable the feature in the unit test or when development in the local environment, then just provide a value different from the havingValue (setting in @ConditionalOnProperty) in a properties file like application.yaml or application.properties like:

# in application.yaml
some:
prefix:
target: false

# in application.properties
some.prefix.target=false

Furthermore, we can override the default bean by providing our own custom bean and make sure it will build before the default configuration:

package our.project.config

@Configuration(proxyBeanMethods = false)
@AutoConfiguration(before = XXXConfiguration.class)
// @AutoConfiguration(beforeName = "XXXConfiguration")
public class CustomXXXConfiguration {

@Bean
SomeComponent1 customSomeComponent1() {
return new customSomeComponent1();
}

@Bean("someComponent2")
SomeComponent2 customSomeComponent2() {
return new CustomSomeComponent2();
}

}

If you found this article valuable, please consider supporting my work by buying me a beer 🍺. Don’t forget to share and clap for this article, and follow me on Medium for future tech stories. Thanks for your support and happy reading!

Your generosity fuels my passion for creating more Java and Spring boot content

How it works

Given a spring boot application class

@SpringBootApplication
public class XXXApplication {
public static void main(String[] args) {
SpringApplication.run(XXXApplication.class, args);
}
}

we all know that the @SpringBootApplication inherits both:

  • @ComponentScan: which will scan all the spring components in the same package name.
  • @EnableAutoConfiguration: use a AutoConfigurationImportSelector to pick up the class that should be auto-configured.

Look into the codes of AutoConfigurationImportSelector and the ImportCandidates, which is used in the former class. we can see that

AutoConfigurationImportSelector
ImportCandidates

As we know that the package of @AutoConfiguration.class is org.springframework.boot.autoconfigure

So, it will try to import the configurations written in the file of META-INF/spring/org.springframework.boot.autoconfigure.imports.

Conclusion

In this article, we introduced how to write an auto-configured common module to make other spring boot application can easily plug and work in a Spring-Boot style. And also provide flexibility for users to enable/disable the feature or make their own custom logic to override the default ones.

References

--

--

Noah Hsu
JavaToDev

Java ServerSide Engr🚀, Focusing on Spring, Toggle system, Kafka, Event Sourcing, and CI/CD. Support my work with a 🍺. https://www.buymeacoffee.com/swbhcjhtyvv