Lightweight (~120KB) wrapper over the JDK's built-in HTTP server.
Features:
- Context abstraction over
HttpExchange
to easily retrieve and send request/response data. - Fluent API
- Static resource handling
- Compression SPI
- Json SPI
- Virtual threads enabled by default
var app = Jex.create()
.routing(routing -> routing
.get("/", ctx -> ctx.text("hello"))
.get("/one/{id}", ctx -> ctx.text("one-" + ctx.pathParam("id")))
.filter(
(ctx, chain) -> {
System.out.println("before request");
chain.proceed();
System.out.println("after request");
})
.error(IllegalStateException.class, (ctx, exception) -> ctx.status(500).text("Handled IllegalStateException|" + exception.getMessage()))
.staticResource(
b ->
b.httpPath("/myResource")
.resource("/public")
.directoryIndex("index.html"))
.port(8080)
.start();
The JDK provides an SPI to swap the underlying HttpServer
, so you can easily use jex with alternate implementations by adding them as a dependency.
An example would be @robaho's implementation where performance seems to be increased by 10x over the default in certain benchmarks.
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-jex</artifactId>
<version>3.0-RC8</version>
</dependency>
<dependency>
<groupId>io.github.robaho</groupId>
<artifactId>httpserver</artifactId>
<version>1.0.10</version>
</dependency>
Use with Avaje Http
If you find yourself pining for the JAX-RS style of controllers, you can have avaje http generate jex adapters for your annotated classes.
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-jex</artifactId>
<version>3.0-RC8</version>
</dependency>
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-http-api</artifactId>
<version>2.9-RC4</version>
</dependency>
<!-- Annotation processor -->
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-http-jex-generator</artifactId>
<version>2.9-RC4</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
In JDK 23+, annotation processors are disabled by default, you will need to add a flag to re-enable.
<properties>
<maven.compiler.proc>full</maven.compiler.proc>
</properties>
package org.example.hello;
import io.avaje.http.api.Controller;
import io.avaje.http.api.Get;
import java.util.List;
@Controller("/widgets")
public class WidgetController {
private final HelloComponent hello;
public WidgetController(HelloComponent hello) {
this.hello = hello;
}
@Get("/{id}")
Widget getById(int id) {
return new Widget(id, "you got it"+ hello.hello());
}
@Get()
List<Widget> getAll() {
return List.of(new Widget(1, "Rob"), new Widget(2, "Fi"));
}
record Widget(int id, String name){};
}
This will generate routing code that we can register using any JSR-330 compliant DI:
@Generated("avaje-jex-generator")
@Singleton
public class WidgetController$Route implements Routing.HttpService {
private final WidgetController controller;
public WidgetController$Route(WidgetController controller) {
this.controller = controller;
}
@Override
public void add(Routing routing) {
routing.get("/widgets/{id}", this::_getById);
routing.get("/widgets", this::_getAll);
}
private void _getById(Context ctx) throws IOException {
ctx.status(200);
var id = asInt(ctx.pathParam("id"));
ctx.json(controller.getById(id));
}
private void _getAll(Context ctx) throws IOException {
ctx.status(200);
ctx.json(controller.getAll());
}
}
You can use whatever DI library you like.
public class Main {
public static void main(String[] args ) {
List<Routing.HttpService> services = // Retrieve HttpServices via DI;
Jex.create().routing(services).start();
}
}
See also:
- Javalin (A lightweight wrapper over Jetty)