Spring Boot starter that provides comprehensive production-ready monitoring and management capabilities for applications
—
The Spring Boot Actuator endpoint framework provides a unified, annotation-based system for creating management endpoints that can be exposed over multiple transports (HTTP, JMX, etc.). It supports both built-in and custom endpoints with flexible access control and operation definitions.
Primary annotation for defining actuator endpoints with configurable access control.
/**
* Annotation for identifying a type as an actuator endpoint
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Endpoint {
/**
* The id of the endpoint (must be unique)
* @return the endpoint id
*/
String id();
/**
* The default access level for the endpoint
* @return the default access level
*/
Access defaultAccess() default Access.RESTRICTED;
/**
* Whether this endpoint should be enabled by default
* @return true if enabled by default
* @deprecated since 3.4.0 in favor of defaultAccess()
*/
@Deprecated(since = "3.4.0")
boolean enableByDefault() default true;
}Annotations for defining different types of endpoint operations.
/**
* Annotation for read-only endpoint operations (HTTP GET)
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ReadOperation {
/**
* The media types produced by the operation
* @return the produced media types
*/
String[] produces() default {};
/**
* The media types produced by the operation, as an enum
* @return the produced media types
*/
WebEndpointHttpMethod[] producesFrom() default {};
}
/**
* Annotation for write endpoint operations (HTTP POST)
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WriteOperation {
/**
* The media types produced by the operation
* @return the produced media types
*/
String[] produces() default {};
/**
* The media types produced by the operation, as an enum
* @return the produced media types
*/
WebEndpointHttpMethod[] producesFrom() default {};
}
/**
* Annotation for delete endpoint operations (HTTP DELETE)
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DeleteOperation {
/**
* The media types produced by the operation
* @return the produced media types
*/
String[] produces() default {};
/**
* The media types produced by the operation, as an enum
* @return the produced media types
*/
WebEndpointHttpMethod[] producesFrom() default {};
}
/**
* Annotation for method parameters that should be treated as path selectors
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Selector {
// Marker annotation - no properties
}Annotations for creating web-specific endpoint extensions and controllers.
/**
* Annotation for web-specific endpoint extensions
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EndpointWebExtension {
/**
* The endpoint class this extension targets
* @return the endpoint class
*/
Class<?> endpoint();
}
/**
* Annotation for controller-based endpoints (DEPRECATED)
* @deprecated since 3.3.0 in favor of @Endpoint and @EndpointWebExtension
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Deprecated(since = "3.3.0")
public @interface ControllerEndpoint {
/**
* The id of the endpoint (must be unique)
* @return the endpoint id
*/
String id();
}Base interfaces that define the endpoint framework structure.
/**
* Interface for endpoints that can be exposed
*/
public interface ExposableEndpoint<O extends Operation> {
/**
* Return the endpoint ID
* @return the endpoint ID
*/
EndpointId getEndpointId();
/**
* Return true if the endpoint is enabled by default
* @return true if enabled by default
*/
boolean isEnableByDefault();
/**
* Return the operations for this endpoint
* @return the operations
*/
Collection<O> getOperations();
}
/**
* A single operation that can be performed on an endpoint
*/
public interface Operation {
/**
* Return the operation type
* @return the operation type
*/
OperationType getType();
/**
* Invoke the operation
* @param context the invocation context
* @return the operation result
*/
Object invoke(InvocationContext context);
}
/**
* Filter for endpoints
*/
@FunctionalInterface
public interface EndpointFilter<E extends ExposableEndpoint<?>> {
/**
* Return true if the endpoint matches the filter
* @param endpoint the endpoint to test
* @return true if the endpoint matches
*/
boolean match(E endpoint);
}Value objects for endpoint identification and access control.
/**
* An endpoint ID
*/
public final class EndpointId {
/**
* Create an endpoint ID from a string
* @param value the endpoint ID value
* @return the endpoint ID
*/
public static EndpointId of(String value) { /* ... */ }
/**
* Get the endpoint ID value
* @return the endpoint ID value
*/
public String toLowerCaseString() { /* ... */ }
/**
* Get the endpoint ID value
* @return the endpoint ID value
*/
@Override
public String toString() { /* ... */ }
}
/**
* Access control levels for endpoints
*/
public enum Access {
/**
* Access is unrestricted
*/
UNRESTRICTED,
/**
* Access is restricted
*/
RESTRICTED
}Security integration for endpoint access control.
/**
* Security context for endpoint operations
*/
public interface SecurityContext {
/**
* Return the principal
* @return the principal or null
*/
Principal getPrincipal();
/**
* Return true if the user is in the specified role
* @param role the role to check
* @return true if the user is in the role
*/
boolean isUserInRole(String role);
}@Endpoint(id = "custom", defaultAccess = Access.UNRESTRICTED)
@Component
public class CustomEndpoint {
private final CustomService customService;
public CustomEndpoint(CustomService customService) {
this.customService = customService;
}
@ReadOperation
public Map<String, Object> info() {
return Map.of(
"status", "operational",
"version", "1.0.0",
"uptime", System.currentTimeMillis()
);
}
@ReadOperation
public Map<String, Object> detailsForId(@Selector String id) {
return customService.getDetailsFor(id);
}
@WriteOperation
public Map<String, Object> updateConfig(@Nullable String key, @Nullable String value) {
if (key != null && value != null) {
customService.updateConfig(key, value);
return Map.of("status", "updated", "key", key);
}
return Map.of("status", "no-change");
}
@DeleteOperation
public Map<String, Object> clearCache() {
customService.clearCache();
return Map.of("status", "cache-cleared");
}
}@EndpointWebExtension(endpoint = CustomEndpoint.class)
@Component
public class CustomWebEndpointExtension {
private final CustomEndpoint delegate;
public CustomWebEndpointExtension(CustomEndpoint delegate) {
this.delegate = delegate;
}
@ReadOperation
public ResponseEntity<Map<String, Object>> infoWithHeaders() {
Map<String, Object> info = delegate.info();
HttpHeaders headers = new HttpHeaders();
headers.add("X-Custom-Header", "custom-value");
headers.add("Cache-Control", "no-cache");
return ResponseEntity.ok()
.headers(headers)
.body(info);
}
@WriteOperation
public ResponseEntity<Map<String, Object>> uploadFile(
@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return ResponseEntity.badRequest()
.body(Map.of("error", "File is required"));
}
// Process file upload
Map<String, Object> result = processFileUpload(file);
return ResponseEntity.ok(result);
}
}@Endpoint(id = "debug", defaultAccess = Access.RESTRICTED)
@ConditionalOnProperty(name = "management.endpoint.debug.enabled", havingValue = "true")
@Component
public class DebugEndpoint {
private final ApplicationContext applicationContext;
public DebugEndpoint(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@ReadOperation
public Map<String, Object> debugInfo() {
return Map.of(
"active-profiles", Arrays.asList(applicationContext.getEnvironment().getActiveProfiles()),
"bean-count", applicationContext.getBeanDefinitionCount(),
"startup-date", new Date(applicationContext.getStartupDate())
);
}
@ReadOperation
public Object beanInfo(@Selector String beanName) {
try {
Object bean = applicationContext.getBean(beanName);
return Map.of(
"class", bean.getClass().getName(),
"singleton", applicationContext.isSingleton(beanName),
"prototype", applicationContext.isPrototype(beanName)
);
} catch (NoSuchBeanDefinitionException e) {
return Map.of("error", "Bean not found: " + beanName);
}
}
}@Endpoint(id = "secure", defaultAccess = Access.RESTRICTED)
@Component
public class SecureEndpoint {
@ReadOperation
public Map<String, Object> publicInfo() {
return Map.of("public", "information");
}
@ReadOperation
public Map<String, Object> sensitiveInfo(SecurityContext securityContext) {
if (securityContext.isUserInRole("ADMIN")) {
return Map.of(
"sensitive", "admin-only-data",
"principal", securityContext.getPrincipal().getName()
);
}
return Map.of("error", "Access denied");
}
}@Component
public class CustomEndpointFilter implements EndpointFilter<ExposableWebEndpoint> {
@Override
public boolean match(ExposableWebEndpoint endpoint) {
// Filter out debug endpoints in production
if (isProductionEnvironment() && "debug".equals(endpoint.getEndpointId().toString())) {
return false;
}
return true;
}
private boolean isProductionEnvironment() {
// Implementation to check environment
return "production".equals(System.getProperty("spring.profiles.active"));
}
}The framework supports several operation types:
/**
* Types of endpoint operations
*/
public enum OperationType {
/**
* Read operation (HTTP GET)
*/
READ,
/**
* Write operation (HTTP POST)
*/
WRITE,
/**
* Delete operation (HTTP DELETE)
*/
DELETE
}Endpoint framework behavior can be configured through application properties:
# Endpoint exposure
management.endpoints.web.exposure.include=health,info,custom
management.endpoints.web.exposure.exclude=
# Base path for web endpoints
management.endpoints.web.base-path=/actuator
# Path mapping for specific endpoints
management.endpoints.web.path-mapping.custom=my-custom-endpoint
# CORS configuration
management.endpoints.web.cors.allowed-origins=*
management.endpoints.web.cors.allowed-methods=GET,POST
# JMX exposure
management.endpoints.jmx.exposure.include=*
# Endpoint specific configuration
management.endpoint.custom.enabled=true
management.endpoint.custom.cache.time-to-live=10sInstall with Tessl CLI
npx tessl i tessl/maven-org-springframework-boot--spring-boot-starter-actuator