How can I control the shutdown order of Spring-managed beans that I don’t create myself?

1 week ago 4
ARTICLE AD BOX

I have a Spring Boot application running in Kubernetes. I’m trying to implement a graceful shutdown flow using a readiness probe:

App receives SIGTERM.

App should start returning 503 from /health so Kubernetes stops sending new traffic.

Existing requests finish.

App shuts down.

I’d like to record metrics for the last few /health calls (especially the 503 responses during shutdown) using Micrometer’s MeterRegistry. However, I’m seeing that the MeterRegistry is already closed while the readiness probe is still hitting /health.

Relevant k8s config:

readinessProbe: initialDelaySeconds: 30 periodSeconds: 1 failureThreshold: 1 httpGet: path: /health port: 9091

Controller:

import io.micrometer.core.instrument.MeterRegistry; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import static org.springframework.http.HttpStatus.SERVICE_UNAVAILABLE; @RestController @RequiredArgsConstructor @Slf4j public class HealthController { private final StatusService statusService; private final MeterRegistry meterRegistry; @GetMapping("/health") public ResponseEntity<String> health() { Status status = statusService.status(); ResponseEntity<String> response = status == Status.OK ? ResponseEntity.ok(status.name()) : ResponseEntity.status(SERVICE_UNAVAILABLE).body(status.name()); int statusCode = response.getStatusCode().value(); String statusCodeString = String.valueOf(statusCode); meterRegistry.counter("health_check_status", "code", statusCodeString).increment(); log.debug("Health check returned: {}. Status code: {}. meterRegistry.isClosed(): {}", response, statusCodeString, meterRegistry.isClosed()); return response; } }

Logs around shutdown:

2025-12-08 13:15:24.096 Health check returned: <200 OK OK,OK,[]>. Status code: 200. meterRegistry.isClosed(): false 2025-12-08 13:15:25.092 Health check returned: <200 OK OK,OK,[]>. Status code: 200. meterRegistry.isClosed(): false 2025-12-08 13:15:26.092 Health check returned: <503 SERVICE_UNAVAILABLE Service Unavailable,SHUTTING_DOWN,[]>. Status code: 503. meterRegistry.isClosed(): true 2025-12-08 13:15:26.095 Health check returned: <503 SERVICE_UNAVAILABLE Service Unavailable,SHUTTING_DOWN,[]>. Status code: 503. meterRegistry.isClosed(): true

So when my StatusService reports SHUTTING_DOWN and /health starts returning 503, meterRegistry.isClosed() is already true, which means I can’t reliably record those 503 metrics.

From what I can see:

MeterRegistry is created and managed by Spring Boot auto-configuration.

On SIGTERM, Spring Boot shuts down and eventually calls MeterRegistry#close().

I don’t directly control the registry bean (no custom bean definition, no @DependsOn, etc.).

Questions

Is there a way in Spring Boot to delay or customize when MeterRegistry.close() is called during shutdown, so that I can still write metrics during the last readiness probe calls?

If not, what is the recommended pattern to:

Keep /health responding with accurate status during shutdown, and

Still record metrics for those responses, when MeterRegistry is managed by Spring Boot?

Read Entire Article