Conflict between HealthEndpointConfiguration and AvailabilityProbesAutoConfiguration #44052
Description
Environment:
- Spring Boot: 3.4.2
- Spring: 6.2.2
- Kubernetes
After upgrading to Spring Boot 3.4 from 3.3 I see an unexpected ClassCastException while calling /health endpoints on Kubernetes, specifically because this works locally as availability probes are not enabled unless in Kubernetes or CloudFoundry or explicitly in properties:
class org.springframework.boot.actuate.autoconfigure.availability.AvailabilityProbesHealthEndpointGroups cannot be cast to class org.springframework.boot.actuate.endpoint.web.AdditionalPathsMapper
While tracing it down, I narrowed the change to #40962 and this commit when additionalPathsMappers were added to WebEndpointDiscoverer
.
HealthEndpointConfiguration
creates a bean of type HealthEndpointGroups
named healthEndpointGroups
with the instance of AutoConfiguredHealthEndpointGroups
that implements HealthEndpointGroups, AdditionalPathsMapper
.
At the same time, AvailabilityProbesAutoConfiguration
creates a post-processor of type AvailabilityProbesHealthEndpointGroupsPostProcessor
that, in turn, also creates a bean of HealthEndpointGroups
but using an instance of AvailabilityProbesHealthEndpointGroups
that implements only HealthEndpointGroups
but not AdditionalPathsMapper
.
When WebEndpointAutoConfiguration
creates webEndpointDiscoverer
it autowires ObjectProvider<AdditionalPathsMapper> additionalPathsMappers
and uses additionalPathsMappers.orderedStream().toList()
but this calls hides the problem due to generics. Calling additionalPathsMappers.getIfAvailable()
fails with BeansNotOfRequiredTypeException
which is correct.
Digging deeper into additionalPathsMappers.orderedStream()
, I see DefaultListableBeanFactory.findAutowireCandidates()
tries to find candidateNames
by AdditionalPathsMapper.class
and gets "healthEndpointGroups"
, but when beanfactory
gets a bean by that name, it is AvailabilityProbesHealthEndpointGroups
which does not implement AdditionalPathsMapper
. Because of generics, this issue is not showing up all the way until DiscoveredWebEndpoint.getAdditionalPaths()
is called because if explicitly requires AdditionalPathsMapper
but gets AvailabilityProbesHealthEndpointGroups
which is not.