Sitemap

Spring Boot — Providing default implementations

2 min readJan 5, 2026

--

Overview

When building libraries, it is sometimes necessary to use standard components like HTTP-clients, JSON-parsers and the like. In some cases it might be interesting to allow users of the library to supply their own implementation to gain more control over the process. This strategy has two benefits. It reduces the amount of flexibility necessary in the library itself and it also helps with testing, as it also allows to swap the component easily with a test dummy.

There are multiple ways how default implementations can be provided. This article will not cover all of them (I’m probably not even aware of all options), but will give three simple options to use.

Backing out

The easiest option is to just back out, to give precedence to an existing bean. This can be done by adding a @ConditionalOnMissingBean annotation to an @Bean annotated method. This will cause the bean only to be created, if no matching bean was provided by the user of the library.

Example:

@AutoConfiguration
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean(MyJsonProcessor.class)
public MyJsonProcessor defaultJsonProcessor() {
return new MyJsonProcessor();
}
}

The above example checks for any bean of type MyJsonProcessor and if there is none, it will create the default instance. Apart from the type, the annotation also allows to check for additional criteria like the bean’s name.

Factory Bean

In some cases it might not be desirable to have a bean of the dependencies type in the IoC context. E.g. if it would duplicate a bean that is regularly expected to be available, like the Jackson ObjectMapper.

Get Christian Frommeyer’s stories in your inbox

Join Medium for free to get updates from this writer.

In this case, it would be possible to decorate the Object with a wrapper, but this will result in a less well known interface and other issues in communicating how to work with the library. It’s easier to instead define a simple interface, that allows to create the object.

public interface MyObjectMapperFactory {
ObjectMapper createMapper();
}

The above interface can then be implemented by a user wishing to customize the ObjectMapper. In the library the factory can be wired either to a @Configuration class for use in multiple beans, or directly into the bean dependent on the ObjectMapper. The library can provide a default either the same way as described above (preferrable), or by making the factory a non-required dependency and creating a new instance in case no factory was available.

Configuration Callback

If it is more likely that a user will want to change the instance rather than creating a new one (e.g. because creating a new instance might be cumbersome), a configuration callback interface might be a good choice. A simple implementation might look like this:

public interface MyJsonProcessorConfigurer {
void configure(MyJsonProcessor processor);
}

@AutoConfiguration
public class MyConfiguration {
@Bean
public MyService myService(@Autowired List<MyJsonProcessorConfigurer> configurers) {
var processor = new MyJsonProcessor();
configurers.forEach(c -> c.configure(processor));
return new MyService(processor);
}
}

With this approach, it would also be possible to ship common customizations as pre defined classes.

--

--