Core infrastructure for endpoint discovery, filtering, parameter mapping, caching, and access control in Spring Boot Actuator.
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.PropertiesEndpointAccessResolver;
import org.springframework.boot.actuate.endpoint.EndpointAccessResolver;
import org.springframework.boot.actuate.endpoint.Access;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
import org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvokerAdvisor;| Class | Package | Purpose |
|---|---|---|
EndpointAutoConfiguration | o.s.b.actuate.autoconfigure.endpoint | Core endpoint infrastructure beans |
PropertiesEndpointAccessResolver | o.s.b.actuate.autoconfigure.endpoint | Property-based access control |
ParameterValueMapper | o.s.b.actuate.endpoint.invoke | Parameter type conversion |
CachingOperationInvokerAdvisor | o.s.b.actuate.endpoint.invoker.cache | Operation caching |
EndpointId | o.s.b.actuate.endpoint | Endpoint identifier |
enum Access {
NONE, // Endpoint disabled
READ_ONLY, // Allows: READ only
UNRESTRICTED // Allows: READ, WRITE, DELETE
}Note: Enum values are shown in logical order (least permissive to most permissive) for clarity. In source code, they are declared in the same order.
@AutoConfiguration
public final class EndpointAutoConfiguration {
// Parameter conversion for endpoint operations
@Bean
@ConditionalOnMissingBean
ParameterValueMapper endpointOperationParameterMapper(
@EndpointConverter ObjectProvider<Converter<?, ?>> converters,
@EndpointConverter ObjectProvider<GenericConverter> genericConverters
);
// Caching advisor for expensive operations
@Bean
@ConditionalOnMissingBean
CachingOperationInvokerAdvisor endpointCachingOperationInvokerAdvisor(Environment environment);
// Access resolver from properties
@Bean
@ConditionalOnMissingBean(EndpointAccessResolver.class)
PropertiesEndpointAccessResolver propertiesEndpointAccessResolver(Environment environment);
}/**
* Resolves endpoint access levels from properties
* Resolution order:
* 1. management.endpoint.<id>.access (specific)
* 2. management.endpoints.access.default (fallback)
* 3. management.endpoints.access.max-permitted (cap)
*
* Implementation Details:
* - Caches resolved access levels per endpoint in a ConcurrentHashMap
* - Initializes default access level and max-permitted access at construction time
* - Supports deprecated 'enabled' and 'enabled-by-default' boolean properties
* - Validates that 'enabled' and 'access' properties are mutually exclusive
*/
public class PropertiesEndpointAccessResolver implements EndpointAccessResolver {
/**
* Creates resolver from property resolver (typically Spring Environment)
*
* Initialization Process:
* 1. Determines default access level from management.endpoints.access.default
* (or deprecated management.endpoints.enabled-by-default)
* 2. Reads max-permitted access from management.endpoints.access.max-permitted
* with default of UNRESTRICTED
* 3. Initializes internal ConcurrentHashMap cache (Map<EndpointId, Access>)
* for thread-safe caching of resolved access levels
*
* @param properties Property resolver for reading configuration
*/
public PropertiesEndpointAccessResolver(PropertyResolver properties);
/**
* Resolves access level for an endpoint using property configuration
*
* Resolution Logic:
* 1. Check cache for previously resolved access
* 2. Read management.endpoint.<id>.access (or deprecated enabled)
* 3. If not set, use management.endpoints.access.default (or deprecated enabled-by-default)
* 4. Apply management.endpoints.access.max-permitted cap
* 5. Cache and return result
*
* Property Precedence:
* - management.endpoint.<id>.access takes precedence over enabled
* - management.endpoints.access.default takes precedence over enabled-by-default
* - Validates that 'enabled' and 'access' are not both set (mutually exclusive)
*
* Deprecated Property Mapping:
* - enabled=true → UNRESTRICTED
* - enabled=false → NONE
* - enabled-by-default=true → UNRESTRICTED
* - enabled-by-default=false → NONE
*
* @param endpointId The endpoint identifier
* @param defaultAccess The default access level if no properties are set
* @return The resolved access level, capped by max-permitted if configured
*/
@Override
public Access accessFor(EndpointId endpointId, Access defaultAccess);
}⚠️ INTERNAL IMPLEMENTATION - NOT FOR PUBLIC USE
This class is package-private and is an internal implementation detail of the caching infrastructure. It is not part of the public API and should not be used directly by application code. It is documented here for completeness and to aid understanding of the caching mechanism.
/**
* Function that resolves cache time-to-live from properties for a given endpoint
* Reads management.endpoint.<id>.cache.time-to-live property
* Package-private internal implementation class - NOT FOR PUBLIC USE
* @since 2.0.0
*/
class EndpointIdTimeToLivePropertyFunction implements Function<EndpointId, @Nullable Long> {
/**
* Creates function that reads TTL from environment properties
* @param environment Spring environment for property resolution
*/
EndpointIdTimeToLivePropertyFunction(Environment environment);
/**
* Gets the cache time-to-live in milliseconds for an endpoint
* Reads from management.endpoint.<id>.cache.time-to-live
* @param endpointId The endpoint identifier
* @return Time-to-live in milliseconds, or null if not configured
*/
@Override
public @Nullable Long apply(EndpointId endpointId);
}Usage Example:
EndpointIdTimeToLivePropertyFunction ttlFunction =
new EndpointIdTimeToLivePropertyFunction(environment);
Long healthTtl = ttlFunction.apply(EndpointId.of("health"));
// Returns value from management.endpoint.health.cache.time-to-live┌─────────────────────────────────────┐
│ 1. Check per-endpoint access │
│ management.endpoint.<id>.access │
└──────────────┬──────────────────────┘
↓ (if not set)
┌─────────────────────────────────────┐
│ 2. Check default access │
│ management.endpoints.access. │
│ default │
└──────────────┬──────────────────────┘
↓
┌─────────────────────────────────────┐
│ 3. Apply max-permitted cap │
│ management.endpoints.access. │
│ max-permitted │
└─────────────────────────────────────┘# Per-endpoint access
management.endpoint.health.access=UNRESTRICTED
management.endpoint.metrics.access=READ_ONLY
management.endpoint.shutdown.access=NONE
# Default access
management.endpoints.access.default=READ_ONLY
# Maximum permitted (caps all endpoints)
management.endpoints.access.max-permitted=READ_ONLY| Scenario | Per-Endpoint | Default | Max-Permitted | Result |
|---|---|---|---|---|
| Explicit UNRESTRICTED | UNRESTRICTED | READ_ONLY | UNRESTRICTED | UNRESTRICTED |
| Capped by max | UNRESTRICTED | READ_ONLY | READ_ONLY | READ_ONLY |
| Use default | (not set) | READ_ONLY | UNRESTRICTED | READ_ONLY |
| Disabled | NONE | UNRESTRICTED | UNRESTRICTED | NONE |
# Cache TTL per endpoint (milliseconds)
management.endpoint.health.cache.time-to-live=10000
management.endpoint.beans.cache.time-to-live=30000
# Disable caching
management.endpoint.metrics.cache.time-to-live=0| Endpoint | Cache? | Typical TTL | Reason |
|---|---|---|---|
| health | Optional | 10s | Changes infrequently |
| metrics | No | 0 | Real-time data |
| beans | Yes | 30-60s | Static after startup |
| conditions | Yes | 60s+ | Never changes |
| env | Yes | 30s | Changes rarely |
| mappings | Yes | 60s+ | Static after startup |
public interface ParameterValueMapper {
/**
* Maps string parameter to required type
* Supports: primitives, enums, common types
* @throws ParameterMappingException if conversion fails
*/
Object mapParameterValue(OperationParameter parameter, @Nullable Object value)
throws ParameterMappingException;
}| From | To | Example |
|---|---|---|
| String | Integer | "123" → 123 |
| String | Boolean | "true" → true |
| String | Enum | "READ" → OperationType.READ |
| String | Duration | "10s" → Duration.ofSeconds(10) |
| String | Custom | Via registered converters |
@Component
public class CustomEndpointAccessResolver implements EndpointAccessResolver {
@Override
public Access accessFor(EndpointId endpointId, Access defaultAccess) {
// Custom logic
if (endpointId.toString().equals("shutdown")) {
return Access.NONE; // Never allow shutdown
}
if (isProductionEnvironment()) {
return Access.READ_ONLY; // Production: read-only
}
return defaultAccess;
}
private boolean isProductionEnvironment() {
return "production".equals(System.getenv("ENV"));
}
}@Component
@EndpointConverter
public class CustomTypeConverter implements Converter<String, CustomType> {
@Override
public CustomType convert(String source) {
return CustomType.parse(source);
}
}Cause: Access level set to NONE or READ_ONLY for write operation
Solution:
management.endpoint.<id>.access=UNRESTRICTEDSymptom: ParameterMappingException when invoking endpoint
Solution:
@Component
@EndpointConverter
public class MyConverter implements Converter<String, MyType> {
@Override
public MyType convert(String source) {
return MyType.valueOf(source);
}
}Cause: Cache TTL not set or set to 0
Solution:
management.endpoint.<id>.cache.time-to-live=10000/**
* Represents an endpoint identifier
* Immutable, normalized representation of an endpoint ID
*/
public final class EndpointId {
/**
* Creates an endpoint ID from a string value
* @param value The endpoint ID value (e.g., "health", "custom-endpoint")
* @return The endpoint ID
* @throws IllegalArgumentException if value is invalid
*/
public static EndpointId of(String value);
/**
* Creates an endpoint ID from a property value
* Normalizes the value (e.g., converts kebab-case/snake_case to lowercase)
* @param value The property value
* @return The endpoint ID
*/
public static EndpointId fromPropertyValue(String value);
/**
* Returns the string representation of this endpoint ID
* @return The endpoint ID as a string
*/
@Override
public String toString();
/**
* Returns the lower-case string representation of this endpoint ID
* @return The endpoint ID as a lower-case string
* @since 2.0.6
*/
public String toLowerCaseString();
/**
* Factory method that respects legacy name migration property
* @param environment Spring environment
* @param value The endpoint ID value
* @return The endpoint ID
* @since 2.2.0
*/
public static EndpointId of(Environment environment, String value);
/**
* Compares this endpoint ID to another object
* @param obj The object to compare
* @return true if equal
*/
@Override
public boolean equals(Object obj);
/**
* Returns the hash code for this endpoint ID
* @return The hash code
*/
@Override
public int hashCode();
}
/**
* Endpoint access levels
* Controls what operations can be performed on an endpoint
* @since 3.4.0
*/
public enum Access {
/**
* UNRESTRICTED - Full access to all operations
* Allows READ, WRITE, and DELETE operations
* Use for: Public endpoints like health, info
*/
UNRESTRICTED,
/**
* READ_ONLY - Read operations only
* Allows READ operations, blocks WRITE and DELETE
* Use for: Endpoints that should be monitored but not modified (beans, env, metrics)
*/
READ_ONLY,
/**
* NONE - Endpoint is disabled
* Blocks all operations, endpoint will not be exposed
* Use for: Disabling specific endpoints (shutdown, threaddump in production)
*/
NONE;
/**
* Caps this access level to the maximum permitted level
* Returns the more restrictive of this access and the maximum permitted
* @param maxPermitted The maximum permitted access level
* @return The capped access level
* @since 3.4.0
*/
public Access cap(Access maxPermitted);
}
/**
* Operation types for endpoints
*/
public enum OperationType {
READ, // Query operation (safe, idempotent) - maps to HTTP GET
WRITE, // Mutating operation - maps to HTTP POST/PUT
DELETE // Deletion operation - maps to HTTP DELETE
}
/**
* Resolves endpoint access levels from configuration
*/
public interface EndpointAccessResolver {
/**
* Determines the access level for an endpoint
* @param endpointId The endpoint identifier
* @param defaultAccess The default access level
* @return The resolved access level
*/
Access accessFor(EndpointId endpointId, Access defaultAccess);
}
/**
* Applies caching behavior to endpoint operations
*/
public interface CachingOperationInvokerAdvisor {
/**
* Applies caching to an operation invoker
* @param endpointId Endpoint ID
* @param operationType Operation type (READ, WRITE, DELETE)
* @param parameters Operation parameters
* @param invoker Original invoker
* @return Wrapped invoker with caching
*/
OperationInvoker apply(
EndpointId endpointId,
OperationType operationType,
OperationParameters parameters,
OperationInvoker invoker
);
}
/**
* Invokes an endpoint operation
*/
@FunctionalInterface
public interface OperationInvoker {
/**
* Invokes the operation
* @param context The invocation context
* @return The operation result
*/
Object invoke(InvocationContext context);
}
/**
* Context for operation invocation
* @since 2.0.0
*/
public class InvocationContext {
/**
* Creates a new context for an operation invocation
* @param securityContext The security context
* @param arguments The arguments available to the operation
* @param argumentResolvers Optional resolvers for additional arguments
*/
public InvocationContext(SecurityContext securityContext, Map<String, Object> arguments,
OperationArgumentResolver... argumentResolvers);
/**
* Gets the operation arguments
* @return Map of argument names to values
*/
public Map<String, Object> getArguments();
/**
* Resolves an argument with the given argument type
* @param <T> Type of the argument
* @param argumentType Type of the argument
* @return Resolved argument of the required type or null
* @since 2.5.0
*/
public <T> T resolveArgument(Class<T> argumentType);
/**
* Returns whether the context is capable of resolving an argument of the given type
* @param type Argument type
* @return true if resolution is possible, otherwise false
* @since 2.5.0
*/
public boolean canResolve(Class<?> type);
}
/**
* Exception thrown when parameter mapping fails
* @since 2.0.0
*/
public final class ParameterMappingException extends InvalidEndpointRequestException {
/**
* Creates a new parameter mapping exception
* @param parameter The parameter being mapped
* @param value The value being mapped
* @param cause The underlying cause
*/
public ParameterMappingException(OperationParameter parameter, @Nullable Object value, Throwable cause);
/**
* Gets the parameter being mapped
* @return The parameter
*/
public OperationParameter getParameter();
/**
* Gets the value being mapped
* @return The value
*/
public @Nullable Object getValue();
}
/**
* Abstract base class for endpoint-related Spring conditions
* Provides property-based evaluation for endpoint enablement
* NOTE: OnAvailableEndpointCondition extends SpringBootCondition directly, not this class
* @since 2.0.0
*/
public abstract class OnEndpointElementCondition extends SpringBootCondition {
/**
* Gets the outcome of the condition
* @param context Condition evaluation context
* @param metadata Annotation metadata
* @return Condition outcome indicating match or no-match
*/
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata);
}EndpointAutoConfiguration
↓ (provides beans to)
┌───────────────────────────────┐
│ Individual Endpoint │
│ Auto-Configurations │
│ (e.g., HealthEndpoint │
│ AutoConfiguration) │
└───────────────────────────────┘
↓ (registers endpoints for)
┌───────────────────────────────┐
│ Exposure Auto-Configurations │
│ (Web, JMX) │
└───────────────────────────────┘@ConditionalOnMissingBean allows customization@AutoConfigureBefore ensures proper orderingREAD_ONLY as default, grant UNRESTRICTED explicitlyEndpointAccessResolver for complex logicFrom Core Infrastructure, you can: