// Core Annotations
org.springframework.boot.actuate.endpoint.annotation.Endpoint
org.springframework.boot.actuate.endpoint.annotation.ReadOperation
org.springframework.boot.actuate.endpoint.annotation.WriteOperation
org.springframework.boot.actuate.endpoint.annotation.DeleteOperation
org.springframework.boot.actuate.endpoint.annotation.Selector
// Web-Specific
org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint
org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension
org.springframework.boot.actuate.endpoint.web.WebEndpointResponse
// Security
org.springframework.boot.actuate.endpoint.SecurityContext
org.springframework.boot.actuate.endpoint.Access
org.springframework.boot.actuate.endpoint.Show
// Audit
org.springframework.boot.actuate.audit.AuditEvent
org.springframework.boot.actuate.audit.AuditEventRepository
org.springframework.boot.actuate.audit.InMemoryAuditEventRepository
// Sanitization
org.springframework.boot.actuate.endpoint.Sanitizer
org.springframework.boot.actuate.endpoint.SanitizingFunction
org.springframework.boot.actuate.endpoint.SanitizableDataNeed to expose information/operations?
│
├─ Needs HTTP-specific features (status codes, headers)?
│ └─ Use @WebEndpoint
│
├─ Needs JMX-only exposure?
│ └─ Use @JmxEndpoint
│
├─ Needs both HTTP and JMX?
│ └─ Use @Endpoint (technology-agnostic)
│
└─ Extending existing endpoint with tech-specific features?
├─ For HTTP: Use @EndpointWebExtension
└─ For JMX: Use @EndpointJmxExtensionWhat does the operation do?
│
├─ Retrieves data without side effects?
│ └─ Use @ReadOperation (maps to GET)
│
├─ Modifies data or triggers action?
│ └─ Use @WriteOperation (maps to POST)
│
└─ Removes data or resources?
└─ Use @DeleteOperation (maps to DELETE)// READ_ONLY - Endpoint has write operations that should be disabled
@Endpoint(id = "config", defaultAccess = Access.READ_ONLY)
// Use when: Endpoint has both read and write ops, but writes are sensitive
// UNRESTRICTED - All operations allowed (default)
@Endpoint(id = "custom", defaultAccess = Access.UNRESTRICTED)
// Use when: Endpoint operations are safe for all authenticated users
// NONE - Endpoint completely disabled by default
@Endpoint(id = "shutdown", defaultAccess = Access.NONE)
// Use when: Endpoint is dangerous and requires explicit enablementPATTERN: Technology-agnostic endpoint
@Endpoint(id = "custom") // Works with HTTP, JMX, etc.
@Component
public class CustomEndpoint {
@ReadOperation
public CustomData getData() {
return new CustomData();
}
}ANTI-PATTERN: HTTP-specific code in @Endpoint
@Endpoint(id = "bad")
@Component
public class BadEndpoint {
@ReadOperation
public ResponseEntity<Data> getData() { // ❌ HTTP-specific
return ResponseEntity.ok(data);
}
}PATTERN: Use web extension for HTTP features
@EndpointWebExtension(endpoint = CustomEndpoint.class)
@Component
public class CustomWebExtension {
@ReadOperation
public WebEndpointResponse<Data> getData() { // ✓ HTTP-specific
return new WebEndpointResponse<>(data, 200);
}
}┌─────────────────────────────────────────────────────────────┐
│ Spring Boot Actuator │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────┐ ┌──────────────────┐ │
│ │ @Endpoint │ │ Built-in │ │
│ │ - Custom Metrics │ │ - Info │ │
│ │ - App Status │ │ - Env │ │
│ │ - Config Ops │ │ - Loggers │ │
│ └─────────┬─────────┘ │ - Beans │ │
│ │ │ - ConfigProps │ │
│ │ │ - AuditEvents │ │
│ │ └────────┬─────────┘ │
│ │ │ │
│ └──────────┬──────────────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ Endpoint │ │
│ │ Discovery │ │
│ │ - Scans beans │ │
│ │ - Creates ops │ │
│ └──────────┬──────────┘ │
│ │ │
│ ┌─────────────┴─────────────┐ │
│ │ │ │
│ ┌──────▼──────┐ ┌────────▼─────┐ │
│ │ Web │ │ JMX │ │
│ │ Exposure │ │ Exposure │ │
│ │ │ │ │ │
│ │ - HTTP │ │ - MBeans │ │
│ │ - REST │ │ - JConsole │ │
│ │ - JSON │ │ - VisualVM │ │
│ └──────┬──────┘ └────────┬─────┘ │
│ │ │ │
│ ┌──────▼──────┐ ┌────────▼─────┐ │
│ │ Security │ │ Security │ │
│ │ - Roles │ │ - Roles │ │
│ │ - Auth │ │ - Auth │ │
│ └─────────────┘ └──────────────┘ │
│ │
│ ┌───────────────────────────────────────────────┐ │
│ │ Cross-Cutting Concerns │ │
│ │ - Sanitization (passwords, secrets) │ │
│ │ - Audit (authentication, authorization) │ │
│ │ - Caching (read operations) │ │
│ │ - Parameter mapping (type conversion) │ │
│ └───────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘Spring Boot Actuator provides production-ready monitoring and management features for Spring Boot applications. It includes built-in endpoints for application information, environment properties, auditing, and application introspection, enabling developers to monitor and interact with applications through HTTP endpoints or JMX. (Note: Health checks and metrics collection are provided by separate spring-boot-health and Micrometer modules.)
Important: Spring Boot Actuator functionality is distributed across multiple modules:
Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>Gradle:
implementation 'org.springframework.boot:spring-boot-starter-actuator'// Endpoint annotations
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
// Web endpoint annotations
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension;
// Core types
import org.springframework.boot.actuate.endpoint.Access;
import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.endpoint.Show;import org.springframework.boot.actuate.endpoint.annotation.*;
import org.springframework.boot.actuate.endpoint.Access;
import org.springframework.stereotype.Component;
// Create a custom actuator endpoint
@Endpoint(id = "custom", defaultAccess = Access.UNRESTRICTED)
@Component
public class CustomEndpoint {
@ReadOperation
public CustomData getData() {
return new CustomData("status", "operational");
}
@WriteOperation
public void updateData(String key, String value) {
// Update logic
}
}
// Access endpoint at: /actuator/custompackage com.example.actuator;
import org.springframework.boot.actuate.endpoint.annotation.*;
import org.springframework.boot.actuate.endpoint.Access;
import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Complete custom endpoint with multiple operations.
* Thread-safe: Yes
* Nullability: All methods return non-null
* Since: Spring Boot 2.0+
*/
@Endpoint(id = "appstatus", defaultAccess = Access.READ_ONLY)
@Component
public class ApplicationStatusEndpoint {
private final AtomicInteger requestCount = new AtomicInteger(0);
private final Instant startTime = Instant.now();
/**
* Get application status.
*
* @return Map containing status information (never null)
*/
@ReadOperation
public Map<String, Object> getStatus() {
Map<String, Object> status = new HashMap<>();
status.put("status", "UP");
status.put("requests", requestCount.get());
status.put("uptime", java.time.Duration.between(startTime, Instant.now()).toString());
return status;
}
/**
* Get status for specific component.
*
* @param component Component name (path variable)
* @return Map containing component status (never null)
*/
@ReadOperation
public Map<String, Object> getComponentStatus(@Selector String component) {
Map<String, Object> status = new HashMap<>();
status.put("component", component);
status.put("status", checkComponent(component));
return status;
}
/**
* Reset request counter (write operation).
* Requires UNRESTRICTED access or explicit enablement.
*
* @param securityContext Security context for authorization check
* @throws SecurityException if user lacks required role
*/
@WriteOperation
public void resetCounter(SecurityContext securityContext) {
if (!securityContext.isUserInRole("ADMIN")) {
throw new SecurityException("ADMIN role required");
}
requestCount.set(0);
}
private String checkComponent(String component) {
// Component check logic
return "HEALTHY";
}
}package com.example.actuator;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.boot.actuate.endpoint.SecurityContext;
import java.security.Principal;
import java.util.Map;
import static org.assertj.core.api.Assertions.*;
class ApplicationStatusEndpointTest {
private ApplicationStatusEndpoint endpoint;
private SecurityContext securityContext;
@BeforeEach
void setUp() {
endpoint = new ApplicationStatusEndpoint();
// Mock security context
securityContext = new SecurityContext() {
@Override
public Principal getPrincipal() {
return () -> "testuser";
}
@Override
public boolean isUserInRole(String role) {
return "ADMIN".equals(role);
}
};
}
@Test
void getStatus_ReturnsValidStatus() {
Map<String, Object> status = endpoint.getStatus();
assertThat(status).isNotNull();
assertThat(status).containsKey("status");
assertThat(status.get("status")).isEqualTo("UP");
assertThat(status).containsKey("requests");
assertThat(status).containsKey("uptime");
}
@Test
void getComponentStatus_WithValidComponent_ReturnsStatus() {
Map<String, Object> status = endpoint.getComponentStatus("database");
assertThat(status).containsKey("component");
assertThat(status.get("component")).isEqualTo("database");
assertThat(status).containsKey("status");
}
@Test
void resetCounter_WithAdminRole_Succeeds() {
// Should not throw exception
assertThatNoException().isThrownBy(() ->
endpoint.resetCounter(securityContext)
);
}
@Test
void resetCounter_WithoutAdminRole_ThrowsException() {
SecurityContext nonAdminContext = new SecurityContext() {
@Override
public Principal getPrincipal() {
return () -> "user";
}
@Override
public boolean isUserInRole(String role) {
return false;
}
};
assertThatThrownBy(() -> endpoint.resetCounter(nonAdminContext))
.isInstanceOf(SecurityException.class)
.hasMessageContaining("ADMIN role required");
}
}Spring Boot Actuator is built around a flexible endpoint architecture:
Access levels and SecurityContextThe framework supports:
Core framework for creating and managing actuator endpoints with support for multiple exposure technologies (HTTP, JMX), operations (read, write, delete), and security integration.
// Endpoint declaration
@interface Endpoint {
String id() default "";
Access defaultAccess() default Access.UNRESTRICTED;
}
@interface WebEndpoint {
String id(); // Aliases to @Endpoint.id() via @AliasFor
Access defaultAccess() default Access.UNRESTRICTED;
}
@interface JmxEndpoint {
String id() default "";
Access defaultAccess() default Access.UNRESTRICTED;
}
// Extension annotations
@interface EndpointExtension {
Class<? extends EndpointFilter<?>> filter();
Class<?> endpoint() default Void.class;
}
@interface EndpointWebExtension {
Class<?> endpoint();
}
@interface EndpointJmxExtension {
Class<?> endpoint();
}
@interface FilteredEndpoint {
Class<? extends EndpointFilter<?>> value();
}
@interface EndpointConverter {
// Marker annotation for endpoint input parameter converters
}
// Operation declarations
@interface ReadOperation {
String[] produces() default {};
Class<? extends Producible> producesFrom() default Producible.class;
}
@interface WriteOperation {
String[] produces() default {};
Class<? extends Producible> producesFrom() default Producible.class;
}
@interface DeleteOperation {
String[] produces() default {};
Class<? extends Producible> producesFrom() default Producible.class;
}
// Parameter selector
@interface Selector {
Match match() default Match.SINGLE;
enum Match {
SINGLE,
ALL_REMAINING
}
}
// Access control
enum Access {
NONE,
READ_ONLY,
UNRESTRICTED;
public Access cap(Access maxPermitted);
}
// Visibility control
enum Show {
NEVER,
WHEN_AUTHORIZED,
ALWAYS;
boolean isShown(boolean unauthorizedResult);
boolean isShown(SecurityContext securityContext, Collection<String> roles);
}
// Operation type classification
enum OperationType {
READ,
WRITE,
DELETE
}
// Security context
interface SecurityContext {
SecurityContext NONE = /* empty context */;
@Nullable Principal getPrincipal();
boolean isUserInRole(String role);
}
// Producible content types
interface Producible<E extends Enum<E> & Producible<E>> {
MimeType getProducedMimeType();
default boolean isDefault() {
return false;
}
}Production-ready endpoints providing monitoring and management capabilities including application information, environment properties, logger configuration, log file access, audit events, beans, configuration properties, auto-configuration conditions, scheduled tasks, thread dumps, heap dumps, HTTP exchanges, request mappings, SBOM, and shutdown. (Note: Health endpoints are in the separate spring-boot-health module.)
// Info endpoint - Application information
@Endpoint(id = "info")
class InfoEndpoint {
InfoEndpoint(List<InfoContributor> infoContributors);
Map<String, Object> info();
}
// Environment endpoint - Environment properties
@Endpoint(id = "env")
class EnvironmentEndpoint {
EnvironmentEndpoint(Environment environment, Iterable<SanitizingFunction> sanitizingFunctions, Show showValues);
EnvironmentDescriptor environment(@Nullable String pattern);
EnvironmentEntryDescriptor environmentEntry(String toMatch);
}
@EndpointWebExtension(endpoint = EnvironmentEndpoint.class)
class EnvironmentEndpointWebExtension {
EnvironmentEndpointWebExtension(EnvironmentEndpoint delegate, Show showValues, Set<String> roles);
EnvironmentDescriptor environment(SecurityContext securityContext, @Nullable String pattern);
WebEndpointResponse<EnvironmentEntryDescriptor> environmentEntry(SecurityContext securityContext, String toMatch);
}
// Loggers endpoint - Logger configuration
@Endpoint(id = "loggers")
class LoggersEndpoint {
LoggersEndpoint(LoggingSystem loggingSystem, LoggerGroups loggerGroups);
LoggersDescriptor loggers();
LoggerLevelsDescriptor loggerLevels(String name);
void configureLogLevel(String name, LogLevel configuredLevel);
}
// Log file endpoint - Log file download
@WebEndpoint(id = "logfile")
class LogFileWebEndpoint {
LogFileWebEndpoint(@Nullable LogFile logFile, @Nullable File externalFile);
@ReadOperation(produces = "text/plain; charset=UTF-8")
@Nullable Resource logFile();
}
// Beans endpoint - Application beans
@Endpoint(id = "beans")
class BeansEndpoint {
BeansEndpoint(ConfigurableApplicationContext context);
BeansDescriptor beans();
}
// Config properties endpoint - Configuration properties
@Endpoint(id = "configprops")
class ConfigurationPropertiesReportEndpoint {
ConfigurationPropertiesReportEndpoint(Iterable<SanitizingFunction> sanitizingFunctions, Show showValues);
ConfigurationPropertiesDescriptor configurationProperties();
ConfigurationPropertiesDescriptor configurationPropertiesWithPrefix(String prefix);
}
@EndpointWebExtension(endpoint = ConfigurationPropertiesReportEndpoint.class)
class ConfigurationPropertiesReportEndpointWebExtension {
ConfigurationPropertiesReportEndpointWebExtension(ConfigurationPropertiesReportEndpoint delegate, Show showValues, Set<String> roles);
ConfigurationPropertiesDescriptor configurationProperties(SecurityContext securityContext);
WebEndpointResponse<ConfigurationPropertiesDescriptor> configurationPropertiesWithPrefix(SecurityContext securityContext, String prefix);
}Flexible audit event tracking and storage framework for recording security events, authentication attempts, authorization decisions, and custom application events with repository abstraction and Spring event integration.
// Core audit types
class AuditEvent {
AuditEvent(@Nullable String principal, String type, Map<String, @Nullable Object> data);
AuditEvent(@Nullable String principal, String type, String... data);
AuditEvent(Instant timestamp, @Nullable String principal, String type, Map<String, @Nullable Object> data);
Instant getTimestamp();
/**
* Returns the principal or empty string if not available.
* NOTE: Never returns null - returns "" when principal is unavailable.
*/
String getPrincipal();
String getType();
Map<String, @Nullable Object> getData();
}
interface AuditEventRepository {
void add(AuditEvent event);
List<AuditEvent> find(@Nullable String principal, @Nullable Instant after, @Nullable String type);
}
class InMemoryAuditEventRepository implements AuditEventRepository {
InMemoryAuditEventRepository();
InMemoryAuditEventRepository(int capacity);
void setCapacity(int capacity);
void add(AuditEvent event);
List<AuditEvent> find(String principal, Instant after, String type);
}
// Audit events endpoint
@Endpoint(id = "auditevents")
class AuditEventsEndpoint {
AuditEventsEndpoint(AuditEventRepository auditEventRepository);
AuditEventsDescriptor events(String principal, OffsetDateTime after, String type);
static final class AuditEventsDescriptor implements OperationResponseBody {
List<AuditEvent> getEvents();
}
}
// Audit event as Spring application event
// Package: org.springframework.boot.actuate.audit.listener
class AuditApplicationEvent extends ApplicationEvent {
AuditApplicationEvent(String principal, String type, Map<String, @Nullable Object> data);
AuditApplicationEvent(String principal, String type, String... data);
AuditApplicationEvent(Instant timestamp, String principal, String type, Map<String, @Nullable Object> data);
AuditApplicationEvent(AuditEvent auditEvent);
AuditEvent getAuditEvent();
}
// Abstract base listener for audit events
// Package: org.springframework.boot.actuate.audit.listener
abstract class AbstractAuditListener implements ApplicationListener<AuditApplicationEvent> {
void onApplicationEvent(AuditApplicationEvent event);
protected abstract void onAuditEvent(AuditEvent event);
}
// Repository-backed audit listener
// Package: org.springframework.boot.actuate.audit.listener
class AuditListener extends AbstractAuditListener {
AuditListener(AuditEventRepository auditEventRepository);
protected void onAuditEvent(AuditEvent event);
}HTTP request-response exchange recording and repository framework for monitoring web traffic, analyzing API usage, and debugging HTTP interactions with configurable capture options and in-memory storage.
// HTTP exchange tracking
class HttpExchange {
HttpExchange(Instant timestamp, Request request, Response response,
@Nullable Principal principal, @Nullable Session session, @Nullable Duration timeTaken);
static Started start(RecordableHttpRequest request);
static Started start(Clock clock, RecordableHttpRequest request);
Instant getTimestamp();
Request getRequest();
Response getResponse();
Principal getPrincipal();
Session getSession();
Duration getTimeTaken();
static class Started {
HttpExchange finish(RecordableHttpResponse response,
Supplier<Principal> principalSupplier,
Supplier<String> sessionIdSupplier,
Include... includes);
}
class Request {
Request(URI uri, @Nullable String remoteAddress, String method, Map<String, List<String>> headers);
String getMethod();
URI getUri();
Map<String, List<String>> getHeaders();
@Nullable String getRemoteAddress();
}
class Response {
Response(int status, Map<String, List<String>> headers);
int getStatus();
Map<String, List<String>> getHeaders();
}
}
enum Include {
REQUEST_HEADERS,
RESPONSE_HEADERS,
COOKIE_HEADERS,
AUTHORIZATION_HEADER,
PRINCIPAL,
SESSION_ID,
TIME_TAKEN,
REMOTE_ADDRESS
}
interface HttpExchangeRepository {
List<HttpExchange> findAll();
void add(HttpExchange httpExchange);
}
class InMemoryHttpExchangeRepository implements HttpExchangeRepository {
InMemoryHttpExchangeRepository();
void setCapacity(int capacity);
void setReverse(boolean reverse);
}Comprehensive framework for protecting sensitive data in endpoint responses with fluent API for pattern matching, flexible sanitization strategies, and built-in detection of credentials, URIs, and sensitive keys.
class Sanitizer {
Sanitizer();
Sanitizer(Iterable<SanitizingFunction> sanitizingFunctions);
@Nullable Object sanitize(SanitizableData data, boolean showUnsanitized);
}
class SanitizableData {
static final String SANITIZED_VALUE = "******";
SanitizableData(PropertySource<?> propertySource, String key, Object value);
PropertySource<?> getPropertySource();
String getKey();
String getLowerCaseKey();
Object getValue();
SanitizableData withSanitizedValue();
SanitizableData withValue(Object value);
}
@FunctionalInterface
interface SanitizingFunction {
SanitizableData apply(SanitizableData data);
// Filter and application control (@since 3.5.0)
default Predicate<SanitizableData> filter();
default SanitizableData applyUnlessFiltered(SanitizableData data);
// High-level detection helpers
default SanitizingFunction ifLikelySensitive();
default SanitizingFunction ifLikelyCredential();
default SanitizingFunction ifLikelyUri();
default SanitizingFunction ifLikelySensitiveProperty(); // @since 3.5.0
default SanitizingFunction ifVcapServices(); // @since 3.5.0
// Key matching methods
default SanitizingFunction ifKeyEquals(String... values);
default SanitizingFunction ifKeyEndsWith(String... suffixes);
default SanitizingFunction ifKeyContains(String... values);
default SanitizingFunction ifKeyMatchesIgnoringCase(BiPredicate<String, String> predicate, String... values); // @since 3.5.0
default SanitizingFunction ifKeyMatches(String... regexes);
default SanitizingFunction ifKeyMatches(Pattern... patterns);
default SanitizingFunction ifKeyMatches(List<Predicate<String>> predicates); // @since 3.5.0
default SanitizingFunction ifKeyMatches(Predicate<String> predicate); // @since 3.5.0
// Value matching methods (@since 3.5.0)
default SanitizingFunction ifValueStringMatches(String... regexes);
default SanitizingFunction ifValueStringMatches(Pattern... patterns);
default SanitizingFunction ifValueStringMatches(List<Predicate<String>> predicates);
default SanitizingFunction ifValueStringMatches(Predicate<String> predicate);
default SanitizingFunction ifValueMatches(List<Predicate<Object>> predicates);
default SanitizingFunction ifValueMatches(Predicate<Object> predicate);
// General data matching (@since 3.5.0)
default SanitizingFunction ifMatches(List<Predicate<SanitizableData>> predicates);
default SanitizingFunction ifMatches(Predicate<SanitizableData> predicate);
// Factory methods
static SanitizingFunction sanitizeValue();
static SanitizingFunction of(SanitizingFunction sanitizingFunction); // @since 3.5.0
}Extensible framework for contributing application information to the info endpoint including build metadata, git information, Java runtime details, OS information, and SSL certificate data.
// Info building
class Info {
Map<String, Object> getDetails();
Object get(String id);
<T> T get(String id, Class<T> type);
static class Builder {
Builder withDetail(String key, Object value);
Builder withDetails(Map<String, Object> details);
Info build();
}
}
@FunctionalInterface
interface InfoContributor {
void contribute(Info.Builder builder);
}
// Base class for property-based contributors
abstract class InfoPropertiesInfoContributor<T extends InfoProperties> implements InfoContributor {
protected InfoPropertiesInfoContributor(T properties, Mode mode);
protected final T getProperties();
protected final Mode getMode();
protected abstract PropertySource<?> toSimplePropertySource();
protected Map<String, Object> generateContent();
protected Map<String, Object> extractContent(PropertySource<?> propertySource);
protected void postProcessContent(Map<String, Object> content);
protected PropertySource<?> toPropertySource();
protected void copyIfSet(Properties target, String key);
protected void replaceValue(Map<String, Object> content, String key, @Nullable Object value);
protected Map<String, Object> getNestedMap(Map<String, Object> map, String key);
enum Mode {
FULL, // Expose all available data including custom properties
SIMPLE // Expose pre-defined core settings only
}
}
// Built-in contributors
class BuildInfoContributor extends InfoPropertiesInfoContributor<BuildProperties> {
BuildInfoContributor(BuildProperties buildProperties);
}
class GitInfoContributor extends InfoPropertiesInfoContributor<GitProperties> {
GitInfoContributor(GitProperties gitProperties);
GitInfoContributor(GitProperties gitProperties, InfoPropertiesInfoContributor.Mode mode);
}
class JavaInfoContributor implements InfoContributor {
JavaInfoContributor();
}
class OsInfoContributor implements InfoContributor {
OsInfoContributor();
}
class ProcessInfoContributor implements InfoContributor {
ProcessInfoContributor();
}
class EnvironmentInfoContributor implements InfoContributor {
EnvironmentInfoContributor(ConfigurableEnvironment environment);
}
class SslInfoContributor implements InfoContributor {
SslInfoContributor(SslInfo sslInfo);
}
class SimpleInfoContributor implements InfoContributor {
SimpleInfoContributor(String prefix, @Nullable Object detail);
}
class MapInfoContributor implements InfoContributor {
MapInfoContributor(Map<String, Object> content);
}Web-specific endpoint support with HTTP method mapping, status code handling, content negotiation, hypermedia links, path mapping, media type configuration, and servlet endpoint registration for Spring MVC and WebFlux.
// Web response wrapper
class WebEndpointResponse<T> {
static final int STATUS_OK = 200;
static final int STATUS_NO_CONTENT = 204;
static final int STATUS_BAD_REQUEST = 400;
static final int STATUS_NOT_FOUND = 404;
static final int STATUS_TOO_MANY_REQUESTS = 429;
static final int STATUS_INTERNAL_SERVER_ERROR = 500;
static final int STATUS_SERVICE_UNAVAILABLE = 503;
WebEndpointResponse();
WebEndpointResponse(int status);
WebEndpointResponse(T body);
WebEndpointResponse(T body, Producible<?> producible);
WebEndpointResponse(T body, MimeType contentType);
WebEndpointResponse(T body, int status);
WebEndpointResponse(T body, int status, MimeType contentType);
@Nullable MimeType getContentType();
@Nullable T getBody();
int getStatus();
}
enum WebEndpointHttpMethod {
GET,
POST,
DELETE
}
// Web endpoint extension
@EndpointWebExtension
@interface EndpointWebExtension {
Class<?> endpoint();
}
// Hypermedia links
class Link {
Link(String href);
String getHref();
boolean isTemplated();
}
class EndpointLinksResolver {
EndpointLinksResolver(Collection<? extends ExposableEndpoint<?>> endpoints);
EndpointLinksResolver(Collection<? extends ExposableEndpoint<?>> endpoints, String basePath);
Map<String, Link> resolveLinks(String requestUrl);
}
// Path mapping
interface PathMappedEndpoint {
String getRootPath();
default List<String> getAdditionalPaths(WebServerNamespace webServerNamespace);
}
interface PathMapper {
@Nullable String getRootPath(EndpointId endpointId);
static String getRootPath(@Nullable List<PathMapper> pathMappers, EndpointId endpointId);
}
class EndpointMapping {
EndpointMapping(String path);
String getPath();
String createSubPath(String path);
}
class PathMappedEndpoints implements Iterable<PathMappedEndpoint> {
PathMappedEndpoints(@Nullable String basePath, EndpointsSupplier<?> supplier);
PathMappedEndpoints(@Nullable String basePath, Collection<EndpointsSupplier<?>> suppliers);
String getBasePath();
@Nullable String getRootPath(EndpointId endpointId);
@Nullable String getPath(EndpointId endpointId);
Collection<String> getAllRootPaths();
Collection<String> getAllPaths();
}
// Media types
class EndpointMediaTypes {
static final EndpointMediaTypes DEFAULT;
EndpointMediaTypes(String... producedAndConsumed);
EndpointMediaTypes(List<String> producedAndConsumed);
EndpointMediaTypes(List<String> produced, List<String> consumed);
List<String> getProduced();
List<String> getConsumed();
}Security context abstraction and audit listeners for authentication and authorization events with role-based access control and Spring Security integration.
// Security context
interface SecurityContext {
SecurityContext NONE = /* empty context */;
@Nullable Principal getPrincipal();
boolean isUserInRole(String role);
}
// Authentication audit
// Package: org.springframework.boot.actuate.security
abstract class AbstractAuthenticationAuditListener
implements ApplicationListener<AbstractAuthenticationEvent>, ApplicationEventPublisherAware {
void setApplicationEventPublisher(ApplicationEventPublisher publisher);
protected ApplicationEventPublisher getPublisher();
protected void publish(AuditEvent event);
}
class AuthenticationAuditListener extends AbstractAuthenticationAuditListener {
static final String AUTHENTICATION_SUCCESS = "AUTHENTICATION_SUCCESS";
static final String AUTHENTICATION_FAILURE = "AUTHENTICATION_FAILURE";
static final String AUTHENTICATION_SWITCH = "AUTHENTICATION_SWITCH";
static final String LOGOUT_SUCCESS = "LOGOUT_SUCCESS";
}
// Authorization audit
// Package: org.springframework.boot.actuate.security
abstract class AbstractAuthorizationAuditListener
implements ApplicationListener<AuthorizationEvent>, ApplicationEventPublisherAware {
void setApplicationEventPublisher(ApplicationEventPublisher publisher);
protected ApplicationEventPublisher getPublisher();
protected void publish(AuditEvent event);
}
class AuthorizationAuditListener extends AbstractAuthorizationAuditListener {
static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE";
}Thread dump, heap dump, and startup information endpoints for runtime diagnostics and performance analysis.
// Thread dump endpoint
@Endpoint(id = "threaddump")
class ThreadDumpEndpoint {
ThreadDumpDescriptor threadDump();
}
// Heap dump endpoint
@WebEndpoint(id = "heapdump", defaultAccess = Access.NONE)
class HeapDumpWebEndpoint {
HeapDumpWebEndpoint();
protected HeapDumpWebEndpoint(long timeout);
@ReadOperation WebEndpointResponse<Resource> heapDump(@Nullable Boolean live);
}
// Startup endpoint
@Endpoint(id = "startup")
class StartupEndpoint {
StartupEndpoint(BufferingApplicationStartup applicationStartup);
@ReadOperation StartupDescriptor startupSnapshot();
@WriteOperation StartupDescriptor startup();
}
// SBOM endpoint
@Endpoint(id = "sbom")
class SbomEndpoint {
SbomEndpoint(SbomProperties properties, ResourceLoader resourceLoader);
@ReadOperation Sboms sboms();
@ReadOperation @Nullable Resource sbom(@Selector String id);
record Sboms(Collection<String> ids) implements OperationResponseBody {}
}
// SBOM web extension - SBOM download with content type
@EndpointWebExtension(endpoint = SbomEndpoint.class)
class SbomEndpointWebExtension {
SbomEndpointWebExtension(SbomEndpoint sbomEndpoint, SbomProperties properties);
@ReadOperation WebEndpointResponse<Resource> sbom(@Selector String id);
}
// SBOM configuration properties
@ConfigurationProperties("management.endpoint.sbom")
class SbomProperties {
Sbom getApplication();
Map<String, Sbom> getAdditional();
void setAdditional(Map<String, Sbom> additional);
static class Sbom {
@Nullable String getLocation();
void setLocation(@Nullable String location);
@Nullable MimeType getMediaType();
void setMediaType(@Nullable MimeType mediaType);
}
}
// Shutdown endpoint
@Endpoint(id = "shutdown", defaultAccess = Access.NONE)
class ShutdownEndpoint implements ApplicationContextAware {
@WriteOperation ShutdownDescriptor shutdown();
}
// Scheduled tasks endpoint
@Endpoint(id = "scheduledtasks")
class ScheduledTasksEndpoint {
ScheduledTasksEndpoint(Collection<ScheduledTaskHolder> scheduledTaskHolders);
@ReadOperation ScheduledTasksDescriptor scheduledTasks();
}
// Request mappings endpoint
@Endpoint(id = "mappings")
class MappingsEndpoint {
MappingsEndpoint(Collection<MappingDescriptionProvider> descriptionProviders, ApplicationContext context);
@ReadOperation ApplicationMappingsDescriptor mappings();
}JMX-specific endpoint support with MBean export, object name generation, and operation response mapping for JMX management tools.
// JMX endpoint annotation
@JmxEndpoint
@interface JmxEndpoint {
String id();
Access defaultAccess() default Access.UNRESTRICTED;
}
@EndpointJmxExtension
@interface EndpointJmxExtension {
Class<?> endpoint();
}
// JMX endpoint exporter
class JmxEndpointExporter {
JmxEndpointExporter(MBeanServer mBeanServer,
EndpointObjectNameFactory objectNameFactory,
JmxOperationResponseMapper responseMapper,
Collection<ExposableJmxEndpoint> endpoints);
}
// Object name factory
@FunctionalInterface
interface EndpointObjectNameFactory {
ObjectName getObjectName(ExposableJmxEndpoint endpoint) throws MalformedObjectNameException;
}
// Response mapper
interface JmxOperationResponseMapper {
Object mapResponse(Object response);
Class<?> mapResponseType(Class<?> responseType);
}
class JacksonJmxOperationResponseMapper implements JmxOperationResponseMapper {
JacksonJmxOperationResponseMapper(JsonMapper jsonMapper);
}Framework for invoking endpoint operations with parameter mapping, reflection-based invocation, caching support, and argument resolution.
// Core invocation
interface OperationInvoker {
Object invoke(InvocationContext context);
}
class InvocationContext {
InvocationContext(SecurityContext securityContext,
Map<String, Object> arguments,
OperationArgumentResolver... argumentResolvers);
Map<String, Object> getArguments();
<T> T resolveArgument(Class<T> argumentType);
boolean canResolve(Class<?> type);
}
// Parameter mapping
interface ParameterValueMapper {
Object mapParameterValue(OperationParameter parameter, Object value);
}
class ConversionServiceParameterValueMapper implements ParameterValueMapper {
ConversionServiceParameterValueMapper();
ConversionServiceParameterValueMapper(ConversionService conversionService);
}
// Reflective invocation
class ReflectiveOperationInvoker implements OperationInvoker {
ReflectiveOperationInvoker(Object target,
OperationMethod operationMethod,
ParameterValueMapper parameterValueMapper);
}
class OperationMethod {
OperationMethod(Method method, OperationType operationType);
Method getMethod();
OperationType getOperationType();
OperationParameters getParameters();
}
// Caching
class CachingOperationInvoker implements OperationInvoker {
CachingOperationInvoker(OperationInvoker invoker, long timeToLive);
Object invoke(InvocationContext context);
}
class CachingOperationInvokerAdvisor implements OperationInvokerAdvisor {
CachingOperationInvokerAdvisor(Function<EndpointId, Long> endpointIdTimeToLive);
}final class EndpointId {
static EndpointId of(String value);
static EndpointId of(Environment environment, String value);
static EndpointId fromPropertyValue(String value);
String toLowerCaseString();
String toString();
boolean equals(Object obj);
int hashCode();
}enum OperationType {
READ,
WRITE,
DELETE
}/**
* Interface implemented by types that produce a specific MIME type.
* Used for content negotiation in operations.
*
* @param <E> enum type that implements this interface
* @since 2.5.0
*/
interface Producible<E extends Enum<E> & Producible<E>> {
/**
* Get the MIME type produced.
*
* @return the MIME type (never null)
*/
MimeType getProducedMimeType();
/**
* Return if this enum value should be used as the default value when an accept
* header of */* is provided, or if the {@code Accept} header is missing.
* Only one value can be marked as default. If no value is marked, then the value
* with the highest {@link Enum#ordinal() ordinal} is used as the default.
*
* @return if this value should be used as the default value
* @since 2.5.6
*/
default boolean isDefault() {
return false;
}
}
enum ApiVersion implements Producible<ApiVersion> {
V2,
V3;
static final ApiVersion LATEST = V3;
MimeType getProducedMimeType();
// isDefault() inherited from Producible interface (both V2 and V3 return false)
}interface OperationResponseBody {
static <K, V> @Nullable Map<K, V> of(@Nullable Map<K, V> map);
}class InvalidEndpointRequestException extends RuntimeException {
InvalidEndpointRequestException(String message, String reason);
InvalidEndpointRequestException(String message, String reason, Throwable cause);
String getReason();
}
class MissingParametersException extends RuntimeException {
MissingParametersException(Set<OperationParameter> missingParameters);
Set<OperationParameter> getMissingParameters();
}
class ParameterMappingException extends RuntimeException {
ParameterMappingException(OperationParameter parameter, Object value, Throwable cause);
}Problem: GET /actuator/custom returns 404
Causes:
Solutions:
// 1. Add @Component annotation
@Endpoint(id = "custom")
@Component // <-- Add this
public class CustomEndpoint { }
// 2. Configure exposure in application.yml
management:
endpoints:
web:
exposure:
include: custom # Add your endpoint ID
// 3. Check endpoint ID matches URL
@Endpoint(id = "custom") // Access at /actuator/customProblem: 403 Forbidden when accessing endpoint
Causes:
Solutions:
// 1. Configure security
@Bean
public SecurityFilterChain actuatorSecurity(HttpSecurity http) {
http.securityMatcher("/actuator/**")
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/custom").hasRole("USER")
.anyRequest().permitAll()
);
return http.build();
}
// 2. Check endpoint access level
@Endpoint(id = "custom", defaultAccess = Access.UNRESTRICTED)
// 3. Enable endpoint explicitly
management:
endpoint:
custom:
enabled: trueProblem: ParameterMappingException when calling endpoint
Causes:
Solutions:
// 1. Add custom converter
@Configuration
public class ConverterConfiguration {
@Bean
public ParameterValueMapper parameterValueMapper() {
DefaultConversionService service = new DefaultConversionService();
service.addConverter(new MyCustomConverter());
return new ConversionServiceParameterValueMapper(service);
}
}
// 2. Make parameter nullable
@ReadOperation
public Data getData(@Nullable String filter) { }
// 3. Document expected format
/**
* @param date Date in ISO format (yyyy-MM-dd'T'HH:mm:ss'Z')
*/
@ReadOperation
public Data getData(OffsetDateTime date) { }// Read operations with no parameters are cacheable
@ReadOperation // Can be cached
public Map<String, Object> getStatus() { }
@ReadOperation // Cannot be cached (has parameters)
public Map<String, Object> search(String query) { }
// Configure caching per endpoint
@Bean
public CachingOperationInvokerAdvisor cachingAdvisor() {
return new CachingOperationInvokerAdvisor(endpointId -> {
switch (endpointId.toLowerCaseString()) {
case "health": return 10_000L; // 10 seconds
case "info": return 60_000L; // 60 seconds
default: return 0L; // No caching
}
});
}// In-memory repositories have capacity limits
InMemoryAuditEventRepository auditRepo =
new InMemoryAuditEventRepository(1000); // Max 1000 events
InMemoryHttpExchangeRepository httpRepo =
new InMemoryHttpExchangeRepository();
httpRepo.setCapacity(500); // Max 500 exchanges
// For production: implement persistent repositories
@Bean
public AuditEventRepository persistentAuditRepo(JdbcTemplate jdbc) {
return new JdbcAuditEventRepository(jdbc);
}// Endpoint beans must be thread-safe
@Endpoint(id = "counter")
@Component
public class CounterEndpoint {
// ✓ Thread-safe
private final AtomicInteger counter = new AtomicInteger(0);
// ❌ NOT thread-safe
// private int counter = 0;
@ReadOperation
public int getCount() {
return counter.get();
}
@WriteOperation
public void increment() {
counter.incrementAndGet();
}
}