How does @Selector work for path parameters, and what does Match.ALL_REMAINING do?
answer
- @Selector = @PathVariable for actuator
- one selector = one segment
- ALL_REMAINING -> String[] tail, must be last
- loggers/{name} is the canonical example
- converted via ApplicationConversionService
basics
~10 s@Selector binds a method parameter to a URL path segment after the endpoint id, e.g. /actuator/loggers/{name}. @Selector(match = Match.ALL_REMAINING) captures all remaining segments into a String array.
solid answer
~40 s@Selector turns a method parameter into a path variable appended after the endpoint's base path. On endpoint id `loggers`, @ReadOperation public X get(@Selector String name) maps to /actuator/loggers/{name}. You can have multiple selectors for successive segments (e.g. /actuator/foo/{a}/{b}). By default a selector matches a single segment. @Selector(match = Match.ALL_REMAINING) makes the last selector greedily capture every remaining segment as a String[] — used when the tail has an unknown number of parts, like a slash-containing key. Selector values go through ApplicationConversionService so you can declare them as enums or numbers, not just String. Selectors work the same on read, write, and delete operations, and they're distinct from non-selector params (query/body). A read with a selector that returns null yields 404, which is the idiomatic 'no such resource' response.
code
java · 16 linesimport org.springframework.boot.actuate.endpoint.annotation.*;
@Component
@Endpoint(id = "config")
public class ConfigEndpoint {
// GET /actuator/config/{key}
@ReadOperation
public String value(@Selector String key) { ... }
// GET /actuator/config/a/b/c -> path = ["a","b","c"]
@ReadOperation
public String nested(@Selector(match = Selector.Match.ALL_REMAINING) String... path) {
return lookup(String.join("/", path));
}
}go deeper
Know @Selector = a path variable like /actuator/loggers/{name}.
Explain single-segment default vs Match.ALL_REMAINING and multiple selectors.
Add type conversion, 400-vs-404 semantics, and selector behavior over JMX.
Reason about URL/key design when identifiers contain slashes and the constraints ALL_REMAINING imposes.
## Purpose `@org.springframework.boot.actuate.endpoint.annotation.Selector` binds a method parameter to a **path segment** that follows the endpoint's base URL. It's Actuator's equivalent of `@PathVariable`. ## Single-segment selectors On endpoint id `loggers`: ```java @ReadOperation public LoggerLevels get(@Selector String name) { ... } ``` maps to `GET /actuator/loggers/{name}` — `GET /actuator/loggers/com.example` binds `name="com.example"`. This is exactly how the built-in `loggers` endpoint exposes per-logger detail. You may declare **multiple** selectors; each consumes one path segment in order: ```java @ReadOperation public X get(@Selector String category, @Selector String key) { ... } // GET /actuator/foo/{category}/{key} ``` ## Type conversion Selector values are converted with `ApplicationConversionService`, so a selector can be typed as an enum, `Integer`, `UUID`, etc., not only `String`. Conversion failure → 400. ## Match.ALL_REMAINING By default a selector matches a **single** segment, so a value containing `/` would be split. When the trailing identifier is itself multi-segment or of unknown length, use: ```java @ReadOperation public X get(@Selector(match = Selector.Match.ALL_REMAINING) String... path) { ... } ``` `Match.ALL_REMAINING` makes this (necessarily the **last**) selector greedily capture **all** remaining path segments into a `String[]` / varargs. Example: a key like `a/b/c` after the base path arrives as `["a", "b", "c"]`, which you rejoin. Only one ALL_REMAINING selector is allowed and it must be last. ## Selectors vs non-selector parameters - `@Selector` → **path**. - No annotation → **query params** (read/delete) or **JSON body** (write). Mixing them is common: `POST /actuator/features/{name}` with body `{"enabled":true}` uses a selector for `name` and a body param for `enabled`. ## Gotchas - Forgetting ALL_REMAINING when your identifier can contain slashes → only the first segment binds, or a routing miss. - ALL_REMAINING must be the final parameter position among selectors. - Selector conversion errors surface as 400, not 404. - Selectors are shared across web and JMX; over JMX they become operation arguments rather than a path.
- Your selector key contains dots and slashes (e.g. 'app/db/url') but only 'app' binds. Fix?A default selector matches a single segment. Use @Selector(match = Match.ALL_REMAINING) String... path to capture all trailing segments, then rejoin them.
- Can a @Selector be typed as something other than String?Yes. Values are converted via ApplicationConversionService, so enums, Integer, UUID, etc. work; a conversion failure produces a 400.
saying these in an interview costs you the question
- Saying @Selector reads from query string
- Believing a single selector captures slash-containing values by default
- Placing ALL_REMAINING before other selectors
- Thinking selectors must always be String