1. 概述

Undertow 是 JBoss 出品的超轻量级高性能 Web 服务器。它同时支持阻塞和非阻塞 NIO API。

由于完全由 Java 编写,可作为嵌入式组件集成到任何 JVM 应用中,甚至 JBoss 的 WildFly 服务器内部也使用 Undertow 来提升性能。

本文将带您快速掌握 Undertow 的核心特性及实战用法。

2. 为什么选择 Undertow?

极致轻量:核心包仅 1MB,嵌入式模式运行时堆内存占用仅需 4MB
Servlet 3.1 完整支持:满足企业级开发需求
WebSocket 原生支持:包括 JSR-356 规范实现
持久连接优化:默认启用 HTTP keep-alive,显著提升客户端性能

3. Undertow 实战

3.1 Maven 依赖

pom.xml 添加核心依赖:

<dependency>
    <groupId>io.undertow</groupId>
    <artifactId>undertow-servlet</artifactId>
    <version>1.4.18.Final</version>
</dependency>

构建可执行 JAR 还需添加 shade 插件:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
</plugin>

最新版本可通过 Maven 中央仓库 获取

3.2 快速启动服务器

通过 Builder API 创建简单 Web 服务:

public class SimpleServer {
    public static void main(String[] args) {
        Undertow server = Undertow.builder()
            .addHttpListener(8080, "localhost")
            .setHandler(exchange -> {
                exchange.getResponseHeaders()
                    .put(Headers.CONTENT_TYPE, "text/plain");
                exchange.getResponseSender().send("Hello Baeldung");
            })
            .build();
        server.start();
    }
}

关键点解析:

  • 绑定 8080 端口到 localhost
  • 使用 Lambda 表达式定义请求处理器
  • 也可用传统匿名类实现(见下文)

⚠️ 核心组件HttpHandler 是定制行为的关键插件。以下为等效的传统写法:

Undertow server = Undertow.builder()
    .addHttpListener(8080, "localhost")
    .setHandler(new HttpHandler() {
        @Override
        public void handleRequest(HttpServerExchange exchange) throws Exception {
            exchange.getResponseHeaders()
                .put(Headers.CONTENT_TYPE, "text/plain");
            exchange.getResponseSender().send("Hello Baeldung");
        }
    })
    .build();

3.3 安全认证实现

3.3.1 身份管理器

实现 IdentityManager 接口处理认证逻辑:

public class CustomIdentityManager implements IdentityManager {
    private Map<String, char[]> users;

    // 标准构造器
    
    @Override
    public Account verify(Account account) {
        return account;
    }
 
    @Override
    public Account verify(Credential credential) {
        return null;
    }
 
    @Override
    public Account verify(String id, Credential credential) {
        Account account = getAccount(id);
        if (account != null && verifyCredential(account, credential)) {
            return account;
        }
        return null;
    }
}

3.3.2 安全链构建

创建安全处理链:

private static HttpHandler addSecurity(
  HttpHandler toWrap, 
  IdentityManager identityManager) {
 
    HttpHandler handler = toWrap;
    handler = new AuthenticationCallHandler(handler);
    handler = new AuthenticationConstraintHandler(handler);
    List<AuthenticationMechanism> mechanisms = Collections.singletonList(
      new BasicAuthenticationMechanism("Baeldung_Realm"));
    handler = new AuthenticationMechanismsHandler(handler, mechanisms);
    handler = new SecurityInitialHandler(
      AuthenticationMode.PRO_ACTIVE, identityManager, handler);
    return handler;
}

认证模式说明:

  • PRO_ACTIVE:所有请求立即触发认证
  • CONSTRAINT_DRIVEN:仅当约束条件触发时认证

3.3.3 集成到服务器

public static void main(String[] args) {
    Map<String, char[]> users = new HashMap<>(2);
    users.put("root", "password".toCharArray());
    users.put("admin", "password".toCharArray());

    IdentityManager idm = new CustomIdentityManager(users);

    Undertow server = Undertow.builder()
        .addHttpListener(8080, "localhost")
        .setHandler(addSecurity(e -> setExchange(e), idm))
        .build();

    server.start();
}

private static void setExchange(HttpServerExchange exchange) {
    SecurityContext context = exchange.getSecurityContext();
    exchange.getResponseSender().send("Hello " + 
      context.getAuthenticatedAccount().getPrincipal().getName(),
      IoCallback.END_EXCHANGE);
}

3.4 WebSocket 支持

通过 WebSocketHttpExchange 创建 Socket 通道:

public static void main(String[] args) {
    Undertow server = Undertow.builder()
        .addHttpListener(8080, "localhost")
        .setHandler(path()
            .addPrefixPath("/baeldungApp", websocket(
                (exchange, channel) -> {
                    channel.getReceiveSetter().set(getListener());
                    channel.resumeReceives();
                }))
            .addPrefixPath("/", resource(new ClassPathResourceManager(
                SocketServer.class.getClassLoader(),
                SocketServer.class.getPackage()))
                .addWelcomeFiles("index.html")))
        .build();

    server.start();
}

private static AbstractReceiveListener getListener() {
    return new AbstractReceiveListener() {
        @Override
        protected void onFullTextMessage(WebSocketChannel channel, 
          BufferedTextMessage message) {
            String messageData = message.getData();
            for (WebSocketChannel session : channel.getPeerConnections()) {
                WebSockets.sendText(messageData, session, null);
            }
        }
    };
}

配套 index.html 可通过原生 JavaScript WebSocket API 连接。

3.5 文件服务器

简单粗暴的文件服务实现:

public static void main( String[] args ) {
    Undertow server = Undertow.builder()
        .addHttpListener(8080, "localhost")
        .setHandler(resource(new PathResourceManager(
            Paths.get(System.getProperty("user.home")), 100))
        .setDirectoryListingEnabled(true))
        .build();
    server.start();
}

自动生成目录列表页面,无需额外开发

4. Spring Boot 集成

除 Tomcat 和 Jetty 外,Spring Boot 同样支持 Undertow 作为嵌入式容器。添加依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
    <version>1.5.6.RELEASE</version>
</dependency>

最新版本见 Maven 中央仓库

5. 总结

本文系统介绍了 Undertow 的核心优势及典型应用场景,包括:

  • 基础服务器搭建
  • 安全认证实现
  • WebSocket 支持
  • 文件服务功能
  • Spring Boot 集成方案

其轻量级特性和高性能表现,特别适合微服务架构及资源敏感型场景。


原始标题:Introduction to JBoss Undertow | Baeldung