REST Server — Programmatic Fluent Builder
As of 10.0, every REST resource class can be configured entirely in code — without placing a @Rest annotation on it
— through the RestBuilder<SELF> / AbstractRestBuilder<R, SELF> CRTP builder API.
Overview
The RestBuilder<SELF> interface is a public, fluent, self-typed configuration surface covering every @Rest
annotation member. AbstractRestBuilder<R, SELF> provides the single shared implementation: each fluent setter writes
into a backing RestAnnotation.Builder (an override bag) and returns self() with the concrete builder's type, so
bespoke setters on a builder subclass chain with true covariant returns without any overriding.
At RestContext construction time, the override bag is serialized into a synthetic highest-priority @Rest
annotation and prepended to the resource's @Rest chain. Builder-supplied values take precedence over annotation
values declared directly on the resource class.
Constructor trio
The framework autodetects which constructor the resource class exposes and uses the most-specific one available:
| Constructor | When used |
|---|---|
MyResource(RestBuilder<?> builder) | Preferred. The builder is passed in; the resource can read builder state or store the builder for later use. |
MyResource() | Fallback no-arg constructor. Builder state is stashed on the instance after construction via an internal setBuilder(AbstractRestBuilder) call. |
(none — RestResource.Builder.build() or static builder() factory) | For flavor builders (RestServlet.Builder, RestResource.Builder, RestMixin.Builder) that construct the resource type via AbstractRestBuilder.createResource(). |
AbstractRestBuilder.createResource() uses ClassInfo reflection to probe for the (RestBuilder<?>) constructor
first, then the no-arg constructor, and throws IllegalStateException if neither is present.
builder(Class<T>) factory autodetection
The recommended idiomatic pattern is to expose a static builder() factory on the resource class:
@Rest(path="/orders")
public class OrderResource extends BasicRestResource {
// Constructor-injection form
public OrderResource(RestBuilder<?> b) { /* read builder if needed */ }
public static RestResource.Builder<OrderResource> builder() {
return RestResource.builder(OrderResource.class);
}
}
RestResource.builder(Class<R>) (and the equivalent RestServlet.builder(Class<R>) / RestMixin.builder(Class<R>))
constructs the appropriate flavor builder parameterized on R, so:
OrderResource r = OrderResource.builder()
.path("/api/v2/orders") // overrides @Rest(path)
.allowedHeaderParams("X-Foo") // overrides @Rest(allowedHeaderParams)
.build();
Builder values win over the class's @Rest annotation for every member that has been set on the builder; unset members
fall through to the annotation as normal.
Building a resource entirely in code (without @Rest)
The most common use case for the programmatic builder is test fixtures and Spring @Bean methods where you want to
configure or override a resource without editing its annotation:
@Configuration
public class AppConfig {
@Bean
Servlet rootServlet() {
return OrderResource.builder()
.path("/api/v2/orders")
.maxInput("5MB")
.build();
}
}
It is also possible to author a resource with no @Rest annotation at all, supplying everything through the builder:
public class MinimalResource extends BasicRestResource {
public MinimalResource(RestBuilder<?> b) {}
public static RestObject.Builder<MinimalResource> builder() {
return RestObject.builder(MinimalResource.class)
.path("/minimal")
.title("Minimal programmatic resource");
}
@RestGet("/ping")
public String ping() { return "pong"; }
}
CRTP self-type
AbstractRestBuilder<R, SELF> implements RestBuilder<SELF> where SELF is the concrete builder type (e.g.
RestServlet.Builder). Every fluent setter returns self() cast to SELF, so builder subclasses adding bespoke
setters can declare them as returning SELF without overriding the base setters:
public class MyServletBuilder extends RestServlet.Builder<MyServletBuilder> {
private String extra;
public MyServletBuilder extra(String v) { this.extra = v; return self(); }
}
MyServlet s = new MyServletBuilder()
.path("/foo")
.extra("bar") // covariant return — no override needed
.build();
See also
- REST Server — Composition (Mixins and Paths) — how resources are composed and mounted.
- @Rest-Annotated Class Basics — the full
@Restattribute reference. - Self-Registration — deploying a programmatically-built resource without
web.xml.