skip to content

What is the difference between @Endpoint, @WebEndpoint, and @JmxEndpoint, and how do @EndpointWebExtension / @EndpointJmxExtension fit in?

level: seniorimportance: should knowfreq 30%

answer

  1. @Endpoint = both; @WebEndpoint = HTTP; @JmxEndpoint = JMX
  2. extension = per-technology add/override, same id
  3. HealthEndpoint + HealthEndpointWebExtension -> 503
  4. WebEndpointResponse belongs in web side
  5. JMX exposure off by default

basics

~10 s

@Endpoint publishes over both HTTP and JMX. @WebEndpoint is HTTP-only; @JmxEndpoint is JMX-only. @EndpointWebExtension / @EndpointJmxExtension add or override operations of an existing endpoint for just one technology.

solid answer

~50 s

@Endpoint is technology-agnostic: the same bean is exposed over both web (HTTP) and JMX. When an endpoint only makes sense for one, use @WebEndpoint (HTTP only) or @JmxEndpoint (JMX only) — their operations are ignored by the other technology. Extensions are the surgical tool: @EndpointWebExtension(endpoint = SomeEndpoint.class) lets you add web-specific behavior to an existing endpoint without touching its technology-agnostic core — the classic case is the built-in health endpoint's web extension that maps health status to HTTP status codes and can return a WebEndpointResponse with a custom code. @EndpointJmxExtension does the same for JMX. Extensions can override an operation for their technology; the base @Endpoint still supplies the shared implementation. This separation keeps a single logical endpoint id while letting each protocol have tailored input/output (e.g. HTTP status, media types) that wouldn't translate to the other.

code

java · 24 lines
java
import org.springframework.boot.actuate.endpoint.annotation.*;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension;

@Component
@Endpoint(id = "license") // shared over web + JMX
public class LicenseEndpoint {
    @ReadOperation
    public License read() { return currentLicense(); }
}

@Component
@EndpointWebExtension(endpoint = LicenseEndpoint.class) // web-only twist
public class LicenseWebExtension {
    private final LicenseEndpoint delegate;
    LicenseWebExtension(LicenseEndpoint delegate) { this.delegate = delegate; }

    @ReadOperation
    public WebEndpointResponse<License> read() {
        License lic = delegate.read();
        int status = lic.isExpired() ? 410 : 200; // HTTP-specific status
        return new WebEndpointResponse<>(lic, status);
    }
}

go deeper

for a junior

Know @Endpoint covers both HTTP and JMX.

for a middle

Distinguish the three annotations and when to restrict to one transport.

for a senior

Explain extensions and the HealthEndpoint web-extension 503 pattern.

for a principal

Design a single logical endpoint with transport-specific behavior while keeping HTTP concerns out of the JMX surface.

## Three endpoint annotations All three live in `org.springframework.boot.actuate.endpoint.annotation` (web/jmx variants under `...web.annotation` / `...jmx.annotation`). | Annotation | Exposed over | Use when | |---|---|---| | `@Endpoint` | **Web + JMX** | The operation is meaningful over both transports | | `@WebEndpoint` | **Web only** | Output/semantics are HTTP-specific (e.g. streams, HTTP status) | | `@JmxEndpoint` | **JMX only** | Only management via JMX/MBeans matters | With `@Endpoint`, the single bean's `@ReadOperation`/`@WriteOperation`/`@DeleteOperation` methods are surfaced as HTTP routes **and** JMX MBean operations. `@WebEndpoint`/`@JmxEndpoint` restrict that to one transport; the other simply never sees the endpoint. ## Why extensions exist Sometimes an endpoint is fundamentally technology-agnostic, but **one** transport needs extra or different behavior. Rewriting the whole endpoint as `@WebEndpoint` would lose JMX. Extensions solve this: - `@EndpointWebExtension(endpoint = TargetEndpoint.class)` — adds/overrides **web** operations for an existing endpoint id. - `@EndpointJmxExtension(endpoint = TargetEndpoint.class)` — same for **JMX**. An extension bean references the base endpoint class. It contributes operation methods that, for its technology, take precedence over the base endpoint's. The base `@Endpoint` still provides the shared logic and the shared id. ## Canonical example: HealthEndpoint Spring Boot's own `HealthEndpoint` is a technology-agnostic `@Endpoint`. Its `HealthEndpointWebExtension` (an `@EndpointWebExtension`) wraps the health result in a `WebEndpointResponse<HealthComponent>` so it can return **HTTP 503** when status is DOWN and honor `management.endpoint.health.show-details` and HTTP-specific concerns. Over JMX, the plain health result is returned without an HTTP status — which wouldn't mean anything there. This is exactly the case extensions were designed for. ## Practical guidance - Default to `@Endpoint`; you get both transports free. - Reach for `@WebEndpoint` when the payload is inherently HTTP (e.g. returning a `Resource`/stream, needing content negotiation) — such returns can't be modeled over JMX. - Use an extension when you want **one** shared id but a transport-specific twist (HTTP status codes, media types) layered on top. ## Gotchas - An extension must target an existing endpoint class via `endpoint = ...`; it doesn't define a new id. - `@WebEndpoint` operations are invisible to JMX and vice versa — don't expect cross-transport availability. - JMX exposure is disabled by default (since Boot 2.2); a `@JmxEndpoint` still needs `management.endpoints.jmx.exposure.include`. - Returning HTTP-specific types (`WebEndpointResponse`, `Resource`) from a plain `@Endpoint` is discouraged because those don't translate to JMX — put them in a `@WebEndpoint` or a web extension.

  • You want the same endpoint over both transports but with an HTTP-specific status code. What's the cleanest design?
    Keep a technology-agnostic @Endpoint for the shared logic, and add an @EndpointWebExtension that returns a WebEndpointResponse with the custom status. JMX keeps the plain result.
  • Why can't you just return a WebEndpointResponse directly from a plain @Endpoint?
    @Endpoint is exposed over JMX too, and WebEndpointResponse / HTTP status codes are meaningless there. HTTP-specific types belong in a @WebEndpoint or a web extension.

saying these in an interview costs you the question

  • Thinking @WebEndpoint operations are also visible over JMX
  • Believing an extension defines a new endpoint id
  • Returning HTTP-specific types from a technology-agnostic @Endpoint
  • Assuming JMX endpoints are exposed by default

context