Standard jakarta.validation annotations, compiled once per type into a flat
check table. No configuration: constraints come from the annotations already on
your types, and ValidationException extends HttpException with status 422 so
the default handler renders it without this extension registering anything.
record CreateUser(@NotBlank @Size(max = 80) String name,
@Email String email,
@Min(18) int age) {}
CreateUser dto = validation.body(req, CreateUser.class);
Annotations only — Hibernate Validator's engine is deliberately absent. It
resolves constraints reflectively per call and pulls ~2 MB plus EL, which is
the per-request cost this module exists to avoid. jakarta.validation-api is
~90 KB of annotations.
The passing path allocates nothing. Constraints resolve at first use into an
opcode plus operands cached in a ClassValue, so there is no map lookup and no
lock. Fields are read through MethodHandles adapted to an exact signature —
(Object)Object for references, (Object)long for primitive integrals — so
invokeExact neither boxes nor builds the argument array Field.get and
Method.invoke allocate. Checks are a flat array walked by a tableswitch rather
than a class hierarchy behind a virtual call. @Size reads a length the object
already knows and @Email scans with indexOf, because Pattern.matcher allocates
a matcher and two int arrays per call. Messages are pre-rendered at compile
time. The violation list and the exception exist only once something fails.
@Pattern is the marked exception: its regex compiles once but matcher()
allocates per call.
Constraints are read from declared fields, so records and plain classes take
one code path — a constraint on a record component propagates to its backing
field.
Jakarta null semantics are exact: only @NotNull rejects null.
flash-ext-openapi now mirrors the same annotations into the generated schema —
minLength, maxLength, minItems, minimum, maximum, pattern, format: email and
required — via an optional jakarta.validation dependency detected at boot. A
type declares its rules once and both the validator and the published contract
read them. An explicit @Schema still wins; the bridge only fills keys nobody
set, and without the annotations on the classpath the bridge class is never
loaded.
flash-ext-jackson is optional too: validate(value) works without it, only
body(req, type) needs a codec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
52 lines
1.9 KiB
XML
52 lines
1.9 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
<modelVersion>4.0.0</modelVersion>
|
|
|
|
<parent>
|
|
<groupId>dev.relism</groupId>
|
|
<artifactId>flash-extensions</artifactId>
|
|
<version>2.1.0-SNAPSHOT</version>
|
|
</parent>
|
|
|
|
<artifactId>flash-ext-validation</artifactId>
|
|
|
|
<dependencies>
|
|
<dependency>
|
|
<groupId>dev.relism</groupId>
|
|
<artifactId>flash</artifactId>
|
|
</dependency>
|
|
<!--
|
|
Annotations only (~90 KB). Hibernate Validator's engine is deliberately absent: it
|
|
resolves constraints reflectively per call and pulls ~2 MB plus EL. This module
|
|
compiles the same annotations into a flat check table once per class instead.
|
|
-->
|
|
<dependency>
|
|
<groupId>jakarta.validation</groupId>
|
|
<artifactId>jakarta.validation-api</artifactId>
|
|
</dependency>
|
|
<!-- Only needed for Validation.body(...); check(...) works without it. -->
|
|
<dependency>
|
|
<groupId>dev.relism</groupId>
|
|
<artifactId>flash-ext-jackson</artifactId>
|
|
<optional>true</optional>
|
|
</dependency>
|
|
<dependency>
|
|
<groupId>org.junit.jupiter</groupId>
|
|
<artifactId>junit-jupiter</artifactId>
|
|
</dependency>
|
|
<dependency>
|
|
<groupId>dev.relism</groupId>
|
|
<artifactId>flash-testing</artifactId>
|
|
<scope>test</scope>
|
|
</dependency>
|
|
<!-- Interop only: proves constraints reach the published schema. -->
|
|
<dependency>
|
|
<groupId>dev.relism</groupId>
|
|
<artifactId>flash-ext-openapi</artifactId>
|
|
<scope>test</scope>
|
|
</dependency>
|
|
</dependencies>
|
|
</project>
|