CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-springframework-boot--spring-boot-actuator

Spring Boot Actuator provides production-ready monitoring and management features including built-in endpoints for application information, environment properties, auditing, and application introspection.

Pending
Overview
Eval results
Files

web-integration.mddocs/

Web Integration

QUICK REFERENCE

Key Classes and Packages

// Core Web Types
org.springframework.boot.actuate.endpoint.web.WebEndpointResponse
org.springframework.boot.actuate.endpoint.web.WebOperation
org.springframework.boot.actuate.endpoint.web.WebOperationRequestPredicate
org.springframework.boot.actuate.endpoint.web.WebEndpointHttpMethod
org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension

// Web Endpoint Discovery
org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer
org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier
org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint

// Hypermedia
org.springframework.boot.actuate.endpoint.web.Link
org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver

// Servlet Support (Spring MVC)
org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping
org.springframework.boot.actuate.endpoint.web.servlet.ServletEndpointRegistrar

// Reactive Support (Spring WebFlux)
org.springframework.boot.actuate.endpoint.web.reactive.WebFluxEndpointHandlerMapping

HTTP Status Code Quick Reference

Status CodeConstantUse When
200STATUS_OKSuccessful operation with body
204STATUS_NO_CONTENTSuccessful operation without body
400STATUS_BAD_REQUESTInvalid input parameters
404STATUS_NOT_FOUNDResource not found
429STATUS_TOO_MANY_REQUESTSRate limit exceeded
500STATUS_INTERNAL_SERVER_ERRORInternal error
503STATUS_SERVICE_UNAVAILABLEService temporarily unavailable

Decision Tree: WebEndpointResponse vs Plain Return

Does operation need HTTP-specific features?
│
├─ YES: Use WebEndpointResponse
│  ├─ Custom status codes (404, 503, etc.)
│  ├─ Custom content types
│  └─ Conditional responses based on request
│
└─ NO: Use plain return type
   └─ Simple data retrieval (always 200 OK)

HTTP Method Mapping

@ReadOperation    → GET
@WriteOperation   → POST
@DeleteOperation  → DELETE

// Path parameters via @Selector
@ReadOperation
public Data get(@Selector String id) { }
// Maps to: GET /actuator/endpoint/{id}

// Query parameters via method parameters
@ReadOperation
public Data search(String query, Integer limit) { }
// Maps to: GET /actuator/endpoint?query=foo&limit=10

Operation Annotations with @WebEndpoint

IMPORTANT: When using @WebEndpoint, operation annotations (@ReadOperation, @WriteOperation, @DeleteOperation) must be imported from the base annotation package, NOT from the web.annotation package:

// CORRECT imports for @WebEndpoint
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;    // ✓ Base package
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;  // ✓ Base package
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation; // ✓ Base package

// INCORRECT - these do not exist
import org.springframework.boot.actuate.endpoint.web.annotation.ReadOperation;    // ✗ Does not exist
import org.springframework.boot.actuate.endpoint.web.annotation.WriteOperation;  // ✗ Does not exist
import org.springframework.boot.actuate.endpoint.web.annotation.DeleteOperation; // ✗ Does not exist

The web.annotation package contains only @WebEndpoint and @EndpointWebExtension. All operation annotations are in the base endpoint.annotation package and work with both technology-agnostic and web-specific endpoints.

AGENT GUIDANCE

When to Use WebEndpointResponse

Use WebEndpointResponse when:

  • Need custom HTTP status codes (404, 503, etc.)
  • Need to set custom content types
  • Response depends on request conditions
  • Want to return empty body with status code

Use plain return type when:

  • Always return 200 OK
  • Don't need custom headers
  • Simple data retrieval
  • Technology-agnostic endpoint

Pattern vs Anti-Pattern

PATTERN: Conditional status codes

@ReadOperation
public WebEndpointResponse<Data> getData(@Selector String id) {
    Data data = repository.findById(id);

    if (data == null) {
        return new WebEndpointResponse<>(
            WebEndpointResponse.STATUS_NOT_FOUND
        );
    }

    return new WebEndpointResponse<>(data, WebEndpointResponse.STATUS_OK);
}

ANTI-PATTERN: Throwing exceptions for flow control

@ReadOperation
public Data getData(@Selector String id) {
    Data data = repository.findById(id);

    if (data == null) {
        throw new RuntimeException("Not found");  // ❌ Wrong status code
    }

    return data;
}

PATTERN: Custom content type

@ReadOperation
public WebEndpointResponse<String> getCsv() {
    String csv = generateCsv();

    return new WebEndpointResponse<>(
        csv,
        WebEndpointResponse.STATUS_OK,
        MimeTypeUtils.parseMimeType("text/csv")
    );
}

PATTERN: Service unavailable response

@ReadOperation
public WebEndpointResponse<Map<String, Object>> getStatus() {
    if (!service.isAvailable()) {
        return new WebEndpointResponse<>(
            Map.of("status", "unavailable"),
            WebEndpointResponse.STATUS_SERVICE_UNAVAILABLE
        );
    }

    return new WebEndpointResponse<>(
        Map.of("status", "available"),
        WebEndpointResponse.STATUS_OK
    );
}

Web-specific endpoint support with HTTP method mapping, status code handling, content negotiation, hypermedia links, and servlet endpoint registration for Spring MVC and WebFlux.

Capabilities

Web Endpoint Response

Wrapper for HTTP-specific endpoint responses with status codes and content types.

/**
 * HTTP response wrapper for web endpoints.
 * Allows returning custom status codes and content types.
 *
 * Thread-safe: Immutable after construction
 * Nullability: Body can be null for empty responses
 * Since: Spring Boot 2.0+
 *
 * @param <T> Response body type
 */
public final class WebEndpointResponse<T> {

    /** HTTP 200 OK - Successful response with body */
    public static final int STATUS_OK = 200;

    /** HTTP 204 No Content - Successful response without body */
    public static final int STATUS_NO_CONTENT = 204;

    /** HTTP 400 Bad Request - Invalid input parameters */
    public static final int STATUS_BAD_REQUEST = 400;

    /** HTTP 404 Not Found - Resource not found */
    public static final int STATUS_NOT_FOUND = 404;

    /** HTTP 429 Too Many Requests - Rate limit exceeded */
    public static final int STATUS_TOO_MANY_REQUESTS = 429;

    /** HTTP 500 Internal Server Error - Server error */
    public static final int STATUS_INTERNAL_SERVER_ERROR = 500;

    /** HTTP 503 Service Unavailable - Service temporarily unavailable */
    public static final int STATUS_SERVICE_UNAVAILABLE = 503;

    /**
     * Create response with 200 OK status and default content type.
     */
    public WebEndpointResponse();

    /**
     * Create response with custom status code and no body.
     *
     * @param status HTTP status code
     */
    public WebEndpointResponse(int status);

    /**
     * Create response with body and 200 OK status.
     *
     * @param body Response body (nullable)
     */
    public WebEndpointResponse(@Nullable T body);

    /**
     * Create response with body, status, and producible content type.
     *
     * @param body Response body (nullable)
     * @param producible Content type producer
     */
    public WebEndpointResponse(@Nullable T body, Producible<?> producible);

    /**
     * Create response with body and custom content type.
     *
     * @param body Response body (nullable)
     * @param contentType MIME type
     */
    public WebEndpointResponse(@Nullable T body, MimeType contentType);

    /**
     * Create response with body and custom status.
     *
     * @param body Response body (nullable)
     * @param status HTTP status code
     */
    public WebEndpointResponse(@Nullable T body, int status);

    /**
     * Create response with body, status, and content type.
     *
     * @param body Response body (nullable)
     * @param status HTTP status code
     * @param contentType MIME type
     */
    public WebEndpointResponse(@Nullable T body, int status, MimeType contentType);

    /**
     * Get the content type.
     *
     * @return MIME type or null for default
     */
    public @Nullable MimeType getContentType();

    /**
     * Get the response body.
     *
     * @return Response body (nullable)
     */
    public @Nullable T getBody();

    /**
     * Get the HTTP status code.
     *
     * @return Status code
     */
    public int getStatus();
}

Web Endpoint Discovery

Discovery and supplier interfaces for finding and providing web endpoints.

/**
 * Discovers and creates web endpoints from annotated classes.
 * Scans the application context for @WebEndpoint and @EndpointWebExtension annotations.
 *
 * Thread-safe: Yes (immutable after construction)
 * Since: Spring Boot 2.0.0
 */
public class WebEndpointDiscoverer extends EndpointDiscoverer<ExposableWebEndpoint, WebOperation>
        implements WebEndpointsSupplier {

    /**
     * Create a new WebEndpointDiscoverer instance.
     *
     * @param applicationContext Source application context to scan
     * @param parameterValueMapper Maps HTTP parameters to operation parameters
     * @param endpointMediaTypes Media types for endpoints
     * @param endpointPathMappers Custom path mappers for endpoint IDs
     * @param additionalPathsMappers Mappers for additional endpoint paths
     * @param invokerAdvisors Advisors to apply to operation invokers
     * @param endpointFilters Filters to apply to discovered endpoints
     * @param operationFilters Filters to apply to discovered operations
     * @since 3.4.0
     */
    public WebEndpointDiscoverer(ApplicationContext applicationContext,
            ParameterValueMapper parameterValueMapper,
            EndpointMediaTypes endpointMediaTypes,
            @Nullable List<PathMapper> endpointPathMappers,
            @Nullable List<AdditionalPathsMapper> additionalPathsMappers,
            Collection<OperationInvokerAdvisor> invokerAdvisors,
            Collection<EndpointFilter<ExposableWebEndpoint>> endpointFilters,
            Collection<OperationFilter<WebOperation>> operationFilters);

    /**
     * Get the discovered web endpoints.
     * Inherited from WebEndpointsSupplier.
     *
     * @return Collection of discovered web endpoints
     */
    @Override
    Collection<ExposableWebEndpoint> getEndpoints();
}

/**
 * Supplier interface for providing web endpoints.
 * Functional interface for components that provide web endpoints.
 *
 * Thread-safe: Implementation dependent
 * Since: Spring Boot 2.0.0
 */
@FunctionalInterface
public interface WebEndpointsSupplier extends EndpointsSupplier<ExposableWebEndpoint> {
    /**
     * Get the web endpoints.
     *
     * @return Collection of web endpoints
     */
    @Override
    Collection<ExposableWebEndpoint> getEndpoints();
}

/**
 * Information about a web endpoint that can be exposed over HTTP.
 * Combines endpoint metadata with path mapping information.
 *
 * Thread-safe: Implementation dependent
 * Since: Spring Boot 2.0.0
 */
public interface ExposableWebEndpoint extends ExposableEndpoint<WebOperation>, PathMappedEndpoint {
    /**
     * Get the endpoint ID.
     * Inherited from ExposableEndpoint.
     *
     * @return Endpoint ID
     */
    EndpointId getEndpointId();

    /**
     * Get the operations for this endpoint.
     * Inherited from ExposableEndpoint.
     *
     * @return Collection of web operations
     */
    Collection<WebOperation> getOperations();

    /**
     * Get the root path for the endpoint.
     * Inherited from PathMappedEndpoint.
     *
     * @return Root path
     */
    String getRootPath();
}

Web Operations

Interface and classes for web-specific endpoint operations with HTTP request predicates.

/**
 * An operation on a web endpoint.
 *
 * Thread-safe: Implementation dependent
 * Since: Spring Boot 2.0.0
 */
public interface WebOperation extends Operation {
    /**
     * Get the ID of the operation that uniquely identifies it within its endpoint.
     *
     * @return Operation ID
     */
    String getId();

    /**
     * Check if the underlying operation is blocking.
     *
     * @return true if the operation is blocking
     */
    boolean isBlocking();

    /**
     * Get the predicate for requests that can be handled by this operation.
     *
     * @return Request predicate
     */
    WebOperationRequestPredicate getRequestPredicate();
}

/**
 * A predicate for a request to an operation on a web endpoint.
 * Encapsulates HTTP method, path, and content negotiation requirements.
 *
 * Thread-safe: Yes (immutable)
 * Since: Spring Boot 2.0.0
 */
public final class WebOperationRequestPredicate {
    /**
     * Create a new operation request predicate.
     *
     * @param path Path for the operation
     * @param httpMethod HTTP method that the operation supports
     * @param consumes Media types that the operation consumes
     * @param produces Media types that the operation produces
     */
    public WebOperationRequestPredicate(String path, WebEndpointHttpMethod httpMethod,
            Collection<String> consumes, Collection<String> produces);

    /**
     * Get the path for the operation.
     *
     * @return Path
     */
    public String getPath();

    /**
     * Get the name of the variable used to catch all remaining path segments.
     *
     * @return Variable name or null
     * @since 2.2.0
     */
    public @Nullable String getMatchAllRemainingPathSegmentsVariable();

    /**
     * Get the HTTP method for the operation.
     *
     * @return HTTP method
     */
    public WebEndpointHttpMethod getHttpMethod();

    /**
     * Get the media types that the operation consumes.
     *
     * @return Consumed media types
     */
    public Collection<String> getConsumes();

    /**
     * Get the media types that the operation produces.
     *
     * @return Produced media types
     */
    public Collection<String> getProduces();
}

Path Mapping

Interfaces and classes for customizing endpoint path mappings and URL structure.

/**
 * Represents a web server namespace for managing different web server contexts.
 * Spring Boot can run management endpoints on a separate port from the main application server,
 * and this class distinguishes between those contexts.
 *
 * Thread-safe: Yes (immutable)
 * Package: org.springframework.boot.actuate.endpoint.web
 * @since 2.6.0
 */
public final class WebServerNamespace {

    /**
     * The main server namespace (application endpoints).
     */
    public static final WebServerNamespace SERVER;

    /**
     * The management server namespace (actuator endpoints on separate port).
     */
    public static final WebServerNamespace MANAGEMENT;

    /**
     * Get the string value of this namespace.
     *
     * @return the namespace value (never null)
     */
    public String getValue();

    /**
     * Create a WebServerNamespace from a string value.
     * Returns SERVER or MANAGEMENT for recognized values, or a custom instance otherwise.
     *
     * @param value the namespace value (may be null for SERVER)
     * @return WebServerNamespace instance (never null)
     */
    public static WebServerNamespace from(@Nullable String value);

    /**
     * Check equality based on the namespace value.
     *
     * @param obj the object to compare (may be null)
     * @return true if equal, false otherwise
     */
    @Override
    public boolean equals(Object obj);

    /**
     * Return hash code based on the namespace value.
     *
     * @return the hash code
     */
    @Override
    public int hashCode();

    /**
     * Return the string representation (same as getValue()).
     *
     * @return the namespace value (never null)
     */
    @Override
    public String toString();
}

/**
 * Interface implemented by endpoints that are mapped to a root web path.
 *
 * @since 2.0.0
 */
@FunctionalInterface
public interface PathMappedEndpoint {
    /**
     * Get the root path of the endpoint (relative to the actuator base path).
     * For example, a root path of "example" would be exposed at "/actuator/example".
     *
     * @return Root path for the endpoint
     */
    String getRootPath();

    /**
     * Get any additional paths for the given web server namespace.
     *
     * @param webServerNamespace Web server namespace
     * @return List of additional paths (empty by default)
     * @since 3.4.0
     */
    default List<String> getAdditionalPaths(WebServerNamespace webServerNamespace);
}

/**
 * Strategy interface for providing custom endpoint ID to path mappings.
 *
 * @since 2.0.0
 */
@FunctionalInterface
public interface PathMapper {
    /**
     * Resolve the root path for the specified endpoint ID.
     * Return null if this mapper doesn't support the given endpoint ID.
     *
     * @param endpointId Endpoint ID to map
     * @return Root path or null
     */
    @Nullable String getRootPath(EndpointId endpointId);

    /**
     * Resolve the root path for an endpoint ID using the given path mappers.
     * Falls back to the endpoint ID itself if no mapper matches.
     *
     * @param pathMappers Path mappers (may be null)
     * @param endpointId Endpoint ID
     * @return Root path
     */
    static String getRootPath(@Nullable List<PathMapper> pathMappers, EndpointId endpointId);
}

/**
 * Base path configuration for endpoints.
 *
 * @since 2.0.0
 */
public class EndpointMapping {
    /**
     * Create endpoint mapping with the given base path.
     *
     * @param path Base path
     */
    public EndpointMapping(String path);

    /**
     * Get the base path to which endpoints are mapped.
     *
     * @return Base path
     */
    public String getPath();

    /**
     * Create a sub-path under this mapping.
     *
     * @param path Sub-path to append
     * @return Full path
     */
    public String createSubPath(String path);
}

/**
 * Collection of path-mapped endpoints with convenience methods for path resolution.
 *
 * @since 2.0.0
 */
public class PathMappedEndpoints implements Iterable<PathMappedEndpoint> {
    /**
     * Create instance from a single endpoint supplier.
     *
     * @param basePath Base path for all endpoints
     * @param supplier Endpoint supplier
     */
    public PathMappedEndpoints(@Nullable String basePath, EndpointsSupplier<?> supplier);

    /**
     * Create instance from multiple endpoint suppliers.
     *
     * @param basePath Base path for all endpoints
     * @param suppliers Endpoint suppliers
     */
    public PathMappedEndpoints(@Nullable String basePath, Collection<EndpointsSupplier<?>> suppliers);

    /**
     * Get the base path for all endpoints.
     *
     * @return Base path
     */
    public String getBasePath();

    /**
     * Get the root path for an endpoint (relative to base path).
     *
     * @param endpointId Endpoint ID
     * @return Root path or null if not found
     */
    public @Nullable String getRootPath(EndpointId endpointId);

    /**
     * Get the full path for an endpoint (including base path).
     *
     * @param endpointId Endpoint ID
     * @return Full path or null if not found
     */
    public @Nullable String getPath(EndpointId endpointId);

    /**
     * Get all root paths (excluding additional paths).
     *
     * @return Collection of root paths
     */
    public Collection<String> getAllRootPaths();

    /**
     * Get all full paths (excluding additional paths).
     *
     * @return Collection of full paths
     */
    public Collection<String> getAllPaths();

    /**
     * Get additional paths for an endpoint in the given namespace.
     *
     * @param webServerNamespace Web server namespace
     * @param endpointId Endpoint ID
     * @return Collection of additional paths
     * @since 3.4.0
     */
    public Collection<String> getAdditionalPaths(WebServerNamespace webServerNamespace, EndpointId endpointId);

    /**
     * Get a specific endpoint by ID.
     *
     * @param endpointId Endpoint ID
     * @return PathMappedEndpoint or null if not found
     */
    public @Nullable PathMappedEndpoint getEndpoint(EndpointId endpointId);

    /**
     * Stream all path-mapped endpoints.
     *
     * @return Stream of endpoints
     */
    public Stream<PathMappedEndpoint> stream();

    @Override
    public Iterator<PathMappedEndpoint> iterator();
}

Hypermedia Support

HAL-formatted links and link resolution for endpoint discovery.

/**
 * Details for a link in a HAL-formatted response.
 * Represents a hyperlink with an href and templated flag.
 *
 * Thread-safe: Yes (immutable)
 * Package: org.springframework.boot.actuate.endpoint.web
 * @since 2.0.0
 */
public class Link {
    /**
     * Creates a new Link with the given href.
     * Automatically sets templated to true if href contains "{".
     *
     * @param href Link href (must not be null)
     */
    public Link(String href);

    /**
     * Returns the href of the link.
     *
     * @return Link href (never null)
     */
    public String getHref();

    /**
     * Returns whether the href is templated.
     * A link is considered templated if its href contains "{".
     *
     * @return true if the href is templated, false otherwise
     */
    public boolean isTemplated();

    @Override
    public String toString();
}

/**
 * A resolver for Links to web endpoints.
 * Resolves endpoint links based on request URLs for HAL-formatted responses.
 *
 * Thread-safe: Yes (immutable after construction)
 * Package: org.springframework.boot.actuate.endpoint.web
 * @since 2.0.0
 */
public class EndpointLinksResolver {
    /**
     * Creates a new EndpointLinksResolver that will resolve links to the given endpoints.
     *
     * @param endpoints Endpoints to resolve links for
     */
    public EndpointLinksResolver(Collection<? extends ExposableEndpoint<?>> endpoints);

    /**
     * Creates a new EndpointLinksResolver that will resolve links to the given endpoints
     * that are exposed beneath the given basePath.
     * Logs the number of endpoints being exposed at INFO level.
     *
     * @param endpoints Endpoints to resolve links for
     * @param basePath Base path of the endpoints
     */
    public EndpointLinksResolver(Collection<? extends ExposableEndpoint<?>> endpoints, String basePath);

    /**
     * Resolves links to the known endpoints based on a request with the given requestUrl.
     * Includes a "self" link to the requestUrl and links to each endpoint.
     *
     * @param requestUrl URL of the request for the endpoint links
     * @return Map of endpoint IDs to Links
     */
    public Map<String, Link> resolveLinks(String requestUrl);
}

Web Server Namespace

Namespace for disambiguating between multiple web servers in the same application.

/**
 * Web server namespace used for disambiguation when multiple web servers
 * are running in the same application (e.g., management context on different port).
 *
 * @since 2.6.0
 */
public final class WebServerNamespace {
    /**
     * Namespace that represents the main server.
     */
    public static final WebServerNamespace SERVER;

    /**
     * Namespace that represents the management server.
     */
    public static final WebServerNamespace MANAGEMENT;

    /**
     * Get the value of the namespace.
     *
     * @return Namespace value
     */
    public String getValue();

    /**
     * Factory method to create a new WebServerNamespace from a value.
     * If the value is empty or null then SERVER is returned.
     *
     * @param value Namespace value or null
     * @return Web server namespace
     */
    public static WebServerNamespace from(@Nullable String value);
}

Additional Paths Mapping

Strategy interface for providing additional paths where endpoints are exposed.

/**
 * Strategy interface used to provide a mapping between an endpoint ID
 * and any additional paths where it will be exposed.
 *
 * @since 3.4.0
 */
@FunctionalInterface
public interface AdditionalPathsMapper {
    /**
     * Resolve the additional paths for the specified endpoint ID and web server namespace.
     *
     * @param endpointId Endpoint ID
     * @param webServerNamespace Web server namespace
     * @return Additional paths or null if this mapper doesn't support the given endpoint ID
     */
    @Nullable List<String> getAdditionalPaths(EndpointId endpointId, WebServerNamespace webServerNamespace);
}

Media Type Configuration

Configuration for endpoint content types.

/**
 * Media types that are by default produced and consumed by endpoints.
 *
 * @since 2.0.0
 */
public class EndpointMediaTypes {
    /**
     * Default media types for this version of Spring Boot.
     */
    public static final EndpointMediaTypes DEFAULT;

    /**
     * Create with the same media types for produced and consumed.
     *
     * @param producedAndConsumed Media types
     */
    public EndpointMediaTypes(String... producedAndConsumed);

    /**
     * Create with the same media types for produced and consumed.
     *
     * @param producedAndConsumed Media types
     */
    public EndpointMediaTypes(List<String> producedAndConsumed);

    /**
     * Create with different media types for produced and consumed.
     *
     * @param produced Media types produced by endpoints
     * @param consumed Media types consumed by endpoints
     */
    public EndpointMediaTypes(List<String> produced, List<String> consumed);

    /**
     * Get the media types produced by endpoints.
     *
     * @return Produced media types
     */
    public List<String> getProduced();

    /**
     * Get the media types consumed by endpoints.
     *
     * @return Consumed media types
     */
    public List<String> getConsumed();
}

Web Handler Mappings

Handler mappings that register web endpoints with Spring MVC and WebFlux.

/**
 * Custom HandlerMapping that makes web endpoints available over HTTP using Spring MVC.
 * Registers discovered endpoints as controller methods and handles request routing.
 *
 * Thread-safe: Yes
 * Framework: Spring MVC (Servlet)
 * Since: Spring Boot 4.0.0
 */
public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerMapping {

    /**
     * Create a new WebMvcEndpointHandlerMapping instance.
     *
     * @param endpointMapping Base mapping for all endpoints (e.g., "/actuator")
     * @param endpoints Web endpoints to expose
     * @param endpointMediaTypes Media types consumed and produced by endpoints
     * @param corsConfiguration CORS configuration for endpoints or null
     * @param linksResolver Resolver for determining links to available endpoints
     * @param shouldRegisterLinksMapping Whether to register the root links endpoint
     */
    public WebMvcEndpointHandlerMapping(EndpointMapping endpointMapping,
            Collection<ExposableWebEndpoint> endpoints,
            EndpointMediaTypes endpointMediaTypes,
            @Nullable CorsConfiguration corsConfiguration,
            EndpointLinksResolver linksResolver,
            boolean shouldRegisterLinksMapping);

    /**
     * Get the web endpoints being mapped.
     * Inherited from AbstractWebMvcEndpointHandlerMapping.
     *
     * @return Collection of web endpoints
     */
    public Collection<ExposableWebEndpoint> getEndpoints();
}

/**
 * Custom HandlerMapping that makes web endpoints available over HTTP using Spring WebFlux.
 * Registers discovered endpoints as reactive handlers with non-blocking request handling.
 *
 * Thread-safe: Yes
 * Framework: Spring WebFlux (Reactive)
 * Since: Spring Boot 4.0.0
 */
public class WebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointHandlerMapping
        implements InitializingBean {

    /**
     * Create a new WebFluxEndpointHandlerMapping instance.
     *
     * @param endpointMapping Base mapping for all endpoints (e.g., "/actuator")
     * @param endpoints Web endpoints to expose
     * @param endpointMediaTypes Media types consumed and produced by endpoints
     * @param corsConfiguration CORS configuration for endpoints or null
     * @param linksResolver Resolver for determining links to available endpoints
     * @param shouldRegisterLinksMapping Whether to register the root links endpoint
     */
    public WebFluxEndpointHandlerMapping(EndpointMapping endpointMapping,
            Collection<ExposableWebEndpoint> endpoints,
            EndpointMediaTypes endpointMediaTypes,
            @Nullable CorsConfiguration corsConfiguration,
            EndpointLinksResolver linksResolver,
            boolean shouldRegisterLinksMapping);

    /**
     * Get the web endpoints being mapped.
     * Inherited from AbstractWebFluxEndpointHandlerMapping.
     *
     * @return Collection of web endpoints
     */
    public Collection<ExposableWebEndpoint> getEndpoints();

    /**
     * Initialize the handler mapping after properties are set.
     * Inherited from InitializingBean.
     */
    @Override
    void afterPropertiesSet();
}

Servlet Endpoints (Deprecated)

DEPRECATED: Servlet endpoint support is deprecated since 3.3.0 and scheduled for removal. Use @Endpoint and @WebEndpoint instead for new development.

Framework for registering servlet-based actuator endpoints (deprecated since 3.3.0). These APIs allow exposing servlets as actuator endpoints but are being phased out in favor of the modern endpoint framework.

/**
 * Contains details of a servlet that is exposed as an actuator endpoint.
 *
 * Package: org.springframework.boot.actuate.endpoint.web
 * Thread-safe: Yes (immutable after construction)
 * @since 2.0.0
 * @deprecated since 3.3.0 in favor of {@code @Endpoint} and {@code @WebEndpoint}
 */
@Deprecated(since = "3.3.0", forRemoval = true)
public final class EndpointServlet {

    /**
     * Create an endpoint servlet from a servlet class.
     *
     * @param servlet Servlet class to instantiate (never null)
     */
    public EndpointServlet(Class<? extends Servlet> servlet);

    /**
     * Create an endpoint servlet from a servlet instance.
     *
     * @param servlet Servlet instance (never null)
     */
    public EndpointServlet(Servlet servlet);

    /**
     * Add a single initialization parameter.
     * Returns a new instance with the parameter added.
     *
     * @param name Parameter name (must not be empty)
     * @param value Parameter value
     * @return New EndpointServlet instance with parameter
     */
    public EndpointServlet withInitParameter(String name, String value);

    /**
     * Add multiple initialization parameters.
     * Returns a new instance with the parameters added.
     *
     * @param initParameters Map of parameter name-value pairs (never null)
     * @return New EndpointServlet instance with parameters
     */
    public EndpointServlet withInitParameters(Map<String, String> initParameters);

    /**
     * Set the loadOnStartup priority for servlet registration.
     * Default value is -1 (load on first request).
     *
     * @param loadOnStartup Initialization priority
     * @return New EndpointServlet instance with loadOnStartup value
     * @since 2.2.0
     */
    public EndpointServlet withLoadOnStartup(int loadOnStartup);
}

/**
 * Information describing an endpoint that can be exposed by registering a servlet.
 *
 * Package: org.springframework.boot.actuate.endpoint.web
 * @since 2.0.0
 * @deprecated since 3.3.0 in favor of {@code @Endpoint} and {@code @WebEndpoint}
 */
@Deprecated(since = "3.3.0", forRemoval = true)
public interface ExposableServletEndpoint extends ExposableEndpoint<Operation>, PathMappedEndpoint {

    /**
     * Return details of the servlet that should be registered.
     *
     * @return The endpoint servlet (never null)
     */
    EndpointServlet getEndpointServlet();
}

/**
 * ServletContextInitializer to register servlet endpoints.
 *
 * Package: org.springframework.boot.actuate.endpoint.web
 * @since 2.0.0
 * @deprecated since 3.3.0 in favor of {@code @Endpoint} and {@code @WebEndpoint} support
 */
@Deprecated(since = "3.3.0", forRemoval = true)
public class ServletEndpointRegistrar implements ServletContextInitializer {

    /**
     * Create a new ServletEndpointRegistrar.
     *
     * @param basePath Base path for servlet endpoints (nullable)
     * @param servletEndpoints Collection of servlet endpoints to register (never null)
     */
    public ServletEndpointRegistrar(@Nullable String basePath,
                                   Collection<ExposableServletEndpoint> servletEndpoints);

    /**
     * Create a new ServletEndpointRegistrar with access resolver.
     *
     * @param basePath Base path for servlet endpoints (nullable)
     * @param servletEndpoints Collection of servlet endpoints to register (never null)
     * @param endpointAccessResolver Resolver for endpoint access control (never null)
     */
    public ServletEndpointRegistrar(@Nullable String basePath,
                                   Collection<ExposableServletEndpoint> servletEndpoints,
                                   EndpointAccessResolver endpointAccessResolver);
}

Migration Guide: Replace servlet endpoints with modern @WebEndpoint:

// OLD (deprecated):
@ServletEndpoint(id = "legacy")
public class LegacyServletEndpoint {
    public EndpointServlet endpoint() {
        return new EndpointServlet(MyServlet.class)
            .withInitParameter("param", "value");
    }
}

// NEW (recommended):
@WebEndpoint(id = "modern")
@Component
public class ModernWebEndpoint {
    @ReadOperation
    public WebEndpointResponse<Data> getData() {
        return new WebEndpointResponse<>(data, 200);
    }
}

COMPLETE WORKING EXAMPLES

Example 1: CRUD Endpoint with Status Codes

package com.example.actuator;

import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension;
import org.springframework.boot.actuate.endpoint.annotation.*;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.stereotype.Component;
import org.jspecify.annotations.Nullable;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Resource management endpoint with full CRUD operations.
 *
 * Thread-safe: Yes (uses ConcurrentHashMap)
 * HTTP-specific: Yes (uses status codes)
 * Since: Application 1.0
 */
@EndpointWebExtension(endpoint = ResourceEndpoint.class)
@Component
public class ResourceWebEndpoint {

    private final Map<String, Resource> resources = new ConcurrentHashMap<>();

    /**
     * List all resources.
     *
     * Returns 200 OK with resource list.
     *
     * @return Web response with resources
     */
    @ReadOperation
    public WebEndpointResponse<Map<String, Resource>> listResources() {
        return new WebEndpointResponse<>(
            Map.copyOf(resources),
            WebEndpointResponse.STATUS_OK
        );
    }

    /**
     * Get specific resource.
     *
     * Returns:
     * - 200 OK if found
     * - 404 Not Found if missing
     *
     * @param id Resource ID
     * @return Web response with resource or error
     */
    @ReadOperation
    public WebEndpointResponse<Resource> getResource(@Selector String id) {
        Resource resource = resources.get(id);

        if (resource == null) {
            return new WebEndpointResponse<>(
                WebEndpointResponse.STATUS_NOT_FOUND
            );
        }

        return new WebEndpointResponse<>(
            resource,
            WebEndpointResponse.STATUS_OK
        );
    }

    /**
     * Create or update resource.
     *
     * Returns:
     * - 200 OK if updated
     * - 400 Bad Request if invalid input
     *
     * @param id Resource ID
     * @param name Resource name
     * @param value Resource value
     * @return Web response with result
     */
    @WriteOperation
    public WebEndpointResponse<Map<String, String>> saveResource(
            @Selector String id,
            String name,
            String value) {

        // Validation
        if (name == null || name.isBlank()) {
            return new WebEndpointResponse<>(
                Map.of("error", "Name is required"),
                WebEndpointResponse.STATUS_BAD_REQUEST
            );
        }

        Resource resource = new Resource(id, name, value);
        resources.put(id, resource);

        return new WebEndpointResponse<>(
            Map.of("status", "saved", "id", id),
            WebEndpointResponse.STATUS_OK
        );
    }

    /**
     * Delete resource.
     *
     * Returns:
     * - 204 No Content if deleted
     * - 404 Not Found if not exists
     *
     * @param id Resource ID
     * @return Web response with status
     */
    @DeleteOperation
    public WebEndpointResponse<Void> deleteResource(@Selector String id) {
        Resource removed = resources.remove(id);

        if (removed == null) {
            return new WebEndpointResponse<>(
                WebEndpointResponse.STATUS_NOT_FOUND
            );
        }

        return new WebEndpointResponse<>(
            WebEndpointResponse.STATUS_NO_CONTENT
        );
    }

    public record Resource(String id, String name, String value) {}
}

// Base endpoint (technology-agnostic)
@Endpoint(id = "resources")
@Component
class ResourceEndpoint {
    // Minimal implementation, extended by web extension
}

Example 2: File Download Endpoint

package com.example.actuator;

import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.boot.actuate.endpoint.Access;
import org.springframework.core.io.Resource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.util.MimeTypeUtils;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * File download endpoint with content type detection.
 *
 * Thread-safe: Yes
 * HTTP-only: Yes (files don't make sense over JMX)
 * Since: Application 1.0
 */
@WebEndpoint(id = "downloads", defaultAccess = Access.READ_ONLY)
@Component
public class FileDownloadEndpoint {

    private final Path downloadsDirectory;

    public FileDownloadEndpoint() {
        this.downloadsDirectory = Path.of("/var/app/downloads");
    }

    /**
     * List available files.
     *
     * @return Web response with file list
     */
    @ReadOperation
    public WebEndpointResponse<Map<String, Object>> listFiles() {
        try {
            List<String> files = Files.list(downloadsDirectory)
                .filter(Files::isRegularFile)
                .map(Path::getFileName)
                .map(Path::toString)
                .collect(Collectors.toList());

            return new WebEndpointResponse<>(
                Map.of("files", files),
                WebEndpointResponse.STATUS_OK
            );
        } catch (IOException e) {
            return new WebEndpointResponse<>(
                Map.of("error", "Failed to list files"),
                WebEndpointResponse.STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Download file with appropriate content type.
     *
     * Returns:
     * - 200 OK with file content
     * - 400 Bad Request if path traversal detected
     * - 404 Not Found if file doesn't exist
     * - 500 Internal Server Error if read fails
     *
     * @param filename File name to download
     * @return Web response with file or error
     */
    @ReadOperation
    public WebEndpointResponse<Resource> downloadFile(@Selector String filename) {
        Path filePath = downloadsDirectory.resolve(filename).normalize();

        // Security check: prevent path traversal
        if (!filePath.startsWith(downloadsDirectory)) {
            return new WebEndpointResponse<>(
                WebEndpointResponse.STATUS_BAD_REQUEST
            );
        }

        File file = filePath.toFile();

        // Check existence
        if (!file.exists() || !file.isFile()) {
            return new WebEndpointResponse<>(
                WebEndpointResponse.STATUS_NOT_FOUND
            );
        }

        // Detect content type
        String contentType;
        try {
            contentType = Files.probeContentType(filePath);
            if (contentType == null) {
                contentType = "application/octet-stream";
            }
        } catch (IOException e) {
            contentType = "application/octet-stream";
        }

        return new WebEndpointResponse<>(
            new FileSystemResource(file),
            WebEndpointResponse.STATUS_OK,
            MimeTypeUtils.parseMimeType(contentType)
        );
    }
}

Example 3: Conditional Response Based on Accept Header

package com.example.actuator;

import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.boot.actuate.endpoint.Producible;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * Report endpoint with multiple output formats.
 *
 * Thread-safe: Yes
 * Content negotiation: Yes (JSON, CSV, XML)
 * Since: Application 1.0
 */
@EndpointWebExtension(endpoint = ReportEndpoint.class)
@Component
public class ReportWebExtension {

    private final ReportEndpoint delegate;

    public ReportWebExtension(ReportEndpoint delegate) {
        this.delegate = delegate;
    }

    /**
     * Get report in requested format.
     *
     * Supports:
     * - application/json (default)
     * - text/csv
     * - application/xml
     *
     * @param format Output format
     * @return Web response in requested format
     */
    @ReadOperation
    public WebEndpointResponse<?> getReport(ReportFormat format) {
        Map<String, Object> data = delegate.generateReport();

        switch (format) {
            case CSV:
                String csv = convertToCsv(data);
                return new WebEndpointResponse<>(
                    csv,
                    WebEndpointResponse.STATUS_OK,
                    MimeTypeUtils.parseMimeType("text/csv")
                );

            case XML:
                String xml = convertToXml(data);
                return new WebEndpointResponse<>(
                    xml,
                    WebEndpointResponse.STATUS_OK,
                    MimeTypeUtils.APPLICATION_XML
                );

            case JSON:
            default:
                return new WebEndpointResponse<>(
                    data,
                    WebEndpointResponse.STATUS_OK,
                    MimeTypeUtils.APPLICATION_JSON
                );
        }
    }

    private String convertToCsv(Map<String, Object> data) {
        // CSV conversion logic
        return "key,value\n" + data.entrySet().stream()
            .map(e -> e.getKey() + "," + e.getValue())
            .reduce((a, b) -> a + "\n" + b)
            .orElse("");
    }

    private String convertToXml(Map<String, Object> data) {
        // XML conversion logic
        return "<?xml version=\"1.0\"?><report>" +
            data.entrySet().stream()
                .map(e -> "<" + e.getKey() + ">" + e.getValue() + "</" + e.getKey() + ">")
                .reduce(String::concat)
                .orElse("") +
            "</report>";
    }

    public enum ReportFormat implements Producible<ReportFormat> {
        JSON(MimeTypeUtils.APPLICATION_JSON),
        CSV(MimeTypeUtils.parseMimeType("text/csv")),
        XML(MimeTypeUtils.APPLICATION_XML);

        private final MimeType mimeType;

        ReportFormat(MimeType mimeType) {
            this.mimeType = mimeType;
        }

        @Override
        public MimeType getProducedMimeType() {
            return mimeType;
        }
    }
}

@Endpoint(id = "report")
@Component
class ReportEndpoint {
    public Map<String, Object> generateReport() {
        return Map.of("total", 100, "active", 85);
    }
}

TESTING EXAMPLES

Test 1: Status Code Handling

package com.example.actuator;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import static org.assertj.core.api.Assertions.*;

class ResourceWebEndpointTest {

    private ResourceWebEndpoint endpoint;

    @BeforeEach
    void setUp() {
        endpoint = new ResourceWebEndpoint();
    }

    @Test
    void getResource_WhenExists_Returns200() {
        // Create resource first
        endpoint.saveResource("test", "Test Resource", "value");

        WebEndpointResponse<ResourceWebEndpoint.Resource> response =
            endpoint.getResource("test");

        assertThat(response.getStatus()).isEqualTo(200);
        assertThat(response.getBody()).isNotNull();
        assertThat(response.getBody().name()).isEqualTo("Test Resource");
    }

    @Test
    void getResource_WhenNotExists_Returns404() {
        WebEndpointResponse<ResourceWebEndpoint.Resource> response =
            endpoint.getResource("nonexistent");

        assertThat(response.getStatus()).isEqualTo(404);
        assertThat(response.getBody()).isNull();
    }

    @Test
    void saveResource_WithInvalidName_Returns400() {
        WebEndpointResponse<Map<String, String>> response =
            endpoint.saveResource("test", "", "value");

        assertThat(response.getStatus()).isEqualTo(400);
        assertThat(response.getBody()).containsKey("error");
    }

    @Test
    void deleteResource_WhenExists_Returns204() {
        endpoint.saveResource("test", "Test", "value");

        WebEndpointResponse<Void> response = endpoint.deleteResource("test");

        assertThat(response.getStatus()).isEqualTo(204);
        assertThat(response.getBody()).isNull();
    }

    @Test
    void deleteResource_WhenNotExists_Returns404() {
        WebEndpointResponse<Void> response = endpoint.deleteResource("nonexistent");

        assertThat(response.getStatus()).isEqualTo(404);
    }
}

Test 2: Content Type Handling

package com.example.actuator;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.util.MimeTypeUtils;
import static org.assertj.core.api.Assertions.*;

class ReportWebExtensionTest {

    private ReportEndpoint endpoint;
    private ReportWebExtension extension;

    @BeforeEach
    void setUp() {
        endpoint = new ReportEndpoint();
        extension = new ReportWebExtension(endpoint);
    }

    @Test
    void getReport_AsJson_ReturnsJsonContentType() {
        WebEndpointResponse<?> response =
            extension.getReport(ReportWebExtension.ReportFormat.JSON);

        assertThat(response.getStatus()).isEqualTo(200);
        assertThat(response.getContentType()).isEqualTo(MimeTypeUtils.APPLICATION_JSON);
        assertThat(response.getBody()).isInstanceOf(Map.class);
    }

    @Test
    void getReport_AsCsv_ReturnsCsvContentType() {
        WebEndpointResponse<?> response =
            extension.getReport(ReportWebExtension.ReportFormat.CSV);

        assertThat(response.getStatus()).isEqualTo(200);
        assertThat(response.getContentType())
            .isEqualTo(MimeTypeUtils.parseMimeType("text/csv"));
        assertThat(response.getBody()).isInstanceOf(String.class);
        assertThat((String) response.getBody()).contains("key,value");
    }

    @Test
    void getReport_AsXml_ReturnsXmlContentType() {
        WebEndpointResponse<?> response =
            extension.getReport(ReportWebExtension.ReportFormat.XML);

        assertThat(response.getStatus()).isEqualTo(200);
        assertThat(response.getContentType()).isEqualTo(MimeTypeUtils.APPLICATION_XML);
        assertThat((String) response.getBody()).startsWith("<?xml");
    }
}

TROUBLESHOOTING

Common Error: Wrong HTTP Status Code

Problem: Endpoint returns 500 when should return 404 or 400

Cause: Not using WebEndpointResponse

Solution:

// ❌ Wrong - throws exception
@ReadOperation
public Data getData(@Selector String id) {
    Data data = repository.findById(id);
    if (data == null) {
        throw new RuntimeException("Not found"); // Returns 500
    }
    return data;
}

// ✓ Correct - returns 404
@ReadOperation
public WebEndpointResponse<Data> getData(@Selector String id) {
    Data data = repository.findById(id);
    if (data == null) {
        return new WebEndpointResponse<>(
            WebEndpointResponse.STATUS_NOT_FOUND
        );
    }
    return new WebEndpointResponse<>(data);
}

Common Error: Content Type Not Set

Problem: Browser downloads JSON instead of displaying it

Cause: Missing or wrong content type

Solution:

// Set explicit content type
@ReadOperation
public WebEndpointResponse<String> getData() {
    return new WebEndpointResponse<>(
        jsonString,
        WebEndpointResponse.STATUS_OK,
        MimeTypeUtils.APPLICATION_JSON  // <-- Explicit content type
    );
}

Common Error: File Download Not Working

Problem: File download returns as JSON or fails

Cause: Wrong return type or content type

Solution:

// ✓ Correct file download
@ReadOperation
public WebEndpointResponse<Resource> downloadFile(@Selector String filename) {
    File file = new File("/path/to/" + filename);

    return new WebEndpointResponse<>(
        new FileSystemResource(file),
        WebEndpointResponse.STATUS_OK,
        MimeTypeUtils.APPLICATION_OCTET_STREAM
    );
}

PERFORMANCE NOTES

Response Caching

// WebEndpointResponse results are cacheable
@ReadOperation
public WebEndpointResponse<Data> getData() {
    // This will be cached if caching configured
    return new WebEndpointResponse<>(expensiveOperation());
}

// Conditional responses disable caching
@ReadOperation
public WebEndpointResponse<Data> getData(SecurityContext ctx) {
    // Not cached (has parameters)
    if (ctx.isUserInRole("ADMIN")) {
        return new WebEndpointResponse<>(fullData);
    }
    return new WebEndpointResponse<>(limitedData);
}

Large Response Bodies

// For large responses, use streaming
@ReadOperation
public WebEndpointResponse<Resource> getLargeFile() {
    // Resource supports streaming
    return new WebEndpointResponse<>(
        new FileSystemResource(largeFile),
        WebEndpointResponse.STATUS_OK
    );
}

// Avoid building large objects in memory
@ReadOperation
public WebEndpointResponse<List<Data>> getAllData() {
    // ❌ Bad - loads everything
    List<Data> all = repository.findAll();

    // ✓ Better - paginate
    List<Data> page = repository.findPage(0, 100);
    return new WebEndpointResponse<>(page);
}

Cross-References

  • For endpoint basics: Endpoint Framework
  • For security context: Security Integration
  • For operation invocation: Operation Invocation
  • For built-in web endpoints: Built-in Endpoints
  • For JMX alternative: JMX Integration

Install with Tessl CLI

npx tessl i tessl/maven-org-springframework-boot--spring-boot-actuator

docs

audit.md

builtin-endpoints.md

endpoint-framework.md

http-exchanges.md

index.md

info-contributors.md

jmx-integration.md

management-operations.md

operation-invocation.md

sanitization.md

security.md

web-integration.md

tile.json