Multiple instances of a bean of one class filled with values from application.properties

I intend to write some HealtCheckContributors for a Spring Boot application using spring-boot-actuator. Hence, I implemented two of them. they are intended for checking the health of different apps, of course, but have a nearly identical structure, except the configuration properties, …

SonarQube complains about that and I wonder if it is possible to have a single health check class but instantiated as many times as defined in application.properties. An example:

application.properties:

# actuator
app1.management.baseUrl=http://localhost:10000
app1.management.name=app1HealthCheckContributor
app2.management.basUrl=http://localhost:10001
app2.management.name=app2HealthCheckContributor

HealthCheckContributor for app1:

@Slf4j
@Component("xxx")
public class App1HealthCheckContributor extends AbstractHealthIndicator {

    private final App1Properties app1Properties;

    public App1HealthCheckContributor(final App1Properties app1Properties) {
        this.app1Properties = app1Properties;
    }

    @Override
    protected void doHealthCheck(Health.Builder builder) {...}
}

…and this code for each HealthCheckContributor only distinct in its appXProperties.

Isn’t it possible to have some kind of base class like:

@Slf4j
@Component()
public class MyHealthCheckContributor extends AbstractHealthIndicator {

    private final MyProperties myProperties;

    public MyHealthCheckContributor(final MyProperties myProperties) {
        this.myProperties = myProperties;
    }

    @Override
    protected void doHealthCheck(Health.Builder builder) {...}
}

and let Spring Boot take care of instantiating two HealthCheckContributors (in our case App1HealthCheckContributor and App2HealthCheckContributor)? This would eliminate code duplication.

An example of the properties class file:

@Slf4j
@Data
@ConfigurationProperties(prefix = "app1.management")
public class App1Properties {
    private String baseUrl;
    private String ...;
}

How can I achieve this and how must an application.properties file looks like to achieve what I intend to do?

The final question: How to test multiple instance creation of a bean of one class filled with values from application.properties?

Answer

Assuming the code in doHealthCheck is exactly the same for all apps to be checked you could do the following.

You would start by creating a single health check class:

@Slf4j
public class AppHealthCheckContributor extends AbstractHealthIndicator {

    private final AppProperties appProperties;

    public App1HealthCheckContributor(final AppProperties appProperties) {
        this.appProperties = appProperties;
    }

    @Override
    protected void doHealthCheck(Health.Builder builder) {...}
}

And the properties model as follows:

@Slf4j
@Data
public class AppProperties {
    private String baseUrl;
    private String name;
}

This means that the configuration would be something like the following (in application.yml):

health-check:
  apps:
    - baseUrl: http://localhost:10000
      name: app1
    - baseUrl: http://localhost:10001
      name: app2

Finally, you would need to create a bean for each app and register them in the application context:

@Slf4j
@Data
@Configuration
@ConfigurationProperties(prefix = "health-check")
public class AllAppPropertiesConfiguration {
    private List<AppProperties> apps;

    @Autowired
    private GenericApplicationContext applicationContext;

    @PostConstruct
    fun init() {
        for (AppProperties app : apps) {
            applicationContext.registerBean(app.getName(), AppHealthCheckContributor.class, app);
        }
    }
}