Spring Boot Health Check - Inheritance

Punter Vicky :

I am trying to use spring actuator for health check. I have few urls for which I need to perform health check but the means to call them are pretty similar. I was trying to create a base class which implements healthindicator and child classes that extends from base which pass the actual url and then call the health method. Is there a better way of doing this?

Base Health Check Class

@Component
public class BaseHealthCheck implements HealthIndicator{

    private String url;

    @Override
    public Health health() {
        // call url
        return Health.up().build();
    }

    private void setUrl(String url){
       this.url = url
    }

}

Child Class 1

@Component
public class Child1HealthCheck extends BaseHealthCheck {

    @Value("${childurl1}")
    private String url;

    @Override
    public Health health() {
        super.health();
    }

    @PostConstruct
    private void setUrl(){
       super.setUrl(url);
    }

}

Child Class 2

@Component
public class Child2HealthCheck extends BaseHealthCheck {

    @Value("${childurl2}")
    private String url;

    @Override
    public Health health() {
        super.health();
    }

    @PostConstruct
    private void setUrl(){
       super.setUrl(url);
    }

}
Roland Weisleder :

The two main disadvantages of your solution IMHO are that you need a new class for each new health check and the use of field injection. Another way to implement this would be the following.

I created a simple class for the "business logic". This class implements HealthIndicator and is not a Spring managed bean. It gets all required dependencies from the constructor to avoid field injection.

public class CustomHealthCheck implements HealthIndicator {

    private final String url; // use URL or URI instead of String?

    public CustomHealthCheck(String url) {
        this.url = url;
    }

    @Override
    public Health health() {
        // call url
        return Health.up().build();
    }
}

To make them work with Spring I created a configuration class that creates Spring beans. So there is no need to create a new class for each new health check that has the same logic as an existing one, only a new method inside this class.

@Configuration
public class CustomHealthChecks {

    @Bean
    public CustomHealthCheck firstHealthCheck(@Value("${url1}") String url) {
        return new CustomHealthCheck(url);
    }

    @Bean
    public CustomHealthCheck secondHealthCheck(@Value("${url2}") String url) {
        return new CustomHealthCheck(url);
    }
}

And a simple test to verify that I have two HealthIndicator objects in my spring context.

@Test
public void produces_expected_health_indicators() {
    ApplicationContextRunner runner = new ApplicationContextRunner();
    runner.withPropertyValues("url1=https://stackoverflow.com/", "url2=https://start.spring.io/")
          .withUserConfiguration(CustomHealthChecks.class)
          .run(context -> {
        assertThat(context).getBeans(HealthIndicator.class).hasSize(2);
    });
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=193793&siteId=1