1. 概述

本文将详细解释 Java Web Start (JWS) 是什么、如何在服务端配置它,以及如何创建一个简单的应用程序。

⚠️ 重要提示:JWS 已从 Oracle JDK 11 开始移除。作为替代方案,建议使用 OpenWebStart

2. 什么是 Java Web Start

JWS 是 Java SE 自带的运行时环境,集成在客户端的 Web 浏览器中,自 Java 5 版本起就已存在。

通过从 Web 服务器下载 JNLP 文件(即 Java Network Launch Protocol),该环境允许我们远程执行其中引用的 JAR 包。

简单来说,这个机制在安装了标准 JRE 的客户端计算机上加载并运行 Java 类。它还支持部分 Jakarta EE 的扩展指令。但客户端的 JRE 会严格应用安全限制,通常会对不受信任的域名、缺少 HTTPS 甚至未签名的 JAR 发出警告。

用户可以从普通网站下载 JNLP 文件来启动 JWS 应用程序。下载后,可通过桌面快捷方式或 Java 缓存查看器直接运行。之后它会下载并执行 JAR 文件。

这种机制非常适合交付非 Web(无 HTML)的图形界面应用,例如:

  • 安全文件传输工具
  • 科学计算器
  • 安全键盘
  • 本地图片浏览器

3. 创建一个简单的 JNLP 应用程序

推荐的做法是将应用程序打包成 WAR 文件部署到常规 Web 服务器。我们只需编写所需的应用程序(通常使用 Swing),将其打包为 JAR 文件,然后将该 JAR 与一个 JNLP 文件一起打包到 WAR 中。JNLP 文件负责引用、下载并执行应用程序的 Main 类。

这与常规的 WAR 打包 Web 应用没有区别,唯一区别是需要一个 JNLP 文件来启用 JWS,如下所示:

3.1. Java 应用程序

先写一个简单的 Java 应用:

public class Hello {
    public static void main(String[] args) {
        JFrame f = new JFrame("main");
        f.setSize(200, 100);
        f.setLocationRelativeTo(null);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JLabel label = new JLabel("Hello World");
        f.add(label);
        f.setVisible(true);
    }
}

这是一个非常基础的 Swing 类,没有添加任何 JWS 特定的代码。

3.2. Web 应用程序

将这个 Swing 类打包成 JAR 文件,并与以下 JNLP 文件一起放入 WAR:

<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" 
  codebase="http://localhost:8080/jnlp-example">
    <information>
        <title>Hello</title>
        <vendor>Example</vendor>
    </information>
    <resources>
        <j2se version="1.2+"/>
        <jar href="hello.jar" main="true" />
    </resources>
    <application-desc/>
</jnlp>

将其命名为 hello.jnlp 并放在 WAR 的任意 Web 文件夹中。JAR 和 WAR 文件都是可下载的,因此无需将 JAR 放在 lib 文件夹。

JNLP 文件中硬编码了最终 JAR 的 URL,这可能导致分发问题。如果更换部署服务器,应用将无法运行。

稍后我们会用 servlet 解决这个问题。现在,先将 JAR 文件放在根目录(与 index.html 同级),并通过锚点链接:

<a href="hello.jnlp">Launch</a>

还需要在 JAR 清单中设置主类。通过配置 pom.xml 中的 JAR 插件实现。同时,将 JAR 文件移出 WEB-INF/lib,因为它仅用于下载而非类加载:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    ...
    <executions>
        <execution>
            <phase>compile</phase>
            <goals>
                <goal>jar</goal>
            </goals>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>
                            com.example.Hello
                        </mainClass>
                    </manifest>
                </archive>
                <outputDirectory>
                    ${project.basedir}/target/jws
                </outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>

4. 特殊配置

4.1. 安全问题

要运行应用程序,必须对 JAR 进行签名。创建有效证书和使用 JAR Sign Maven 插件超出了本文范围,但开发环境或拥有用户计算机管理权限时,可以绕过此安全策略。

方法是将本地 URL(如 http://localhost:8080)添加到目标计算机 JRE 安装的安全例外列表中。在 Windows 上,可通过控制面板打开 Java 控制面板,在安全选项卡中找到此设置。

5. JnlpDownloadServlet

5.1. 压缩算法

有一个特殊的 servlet 可以包含在 WAR 中。它通过查找最压缩的 JAR 文件版本来优化下载,并修复 JNLP 文件中硬编码的 codebase 值。

由于 JAR 需要提供下载,建议使用压缩算法(如 Pack200)打包,并将常规 JAR 与 JAR.PACK.GZ 或 JAR.GZ 压缩版本放在同一目录,让 servlet 选择最佳选项。

⚠️ 目前尚无稳定的 Maven 插件支持此压缩算法,但可以使用 JRE 自带的 Pack200 可执行文件(通常位于 *{JAVA_SDK_HOME}/jre/bin/*)。

无需修改 JNLP 文件,只需将 JAR 的 jar.gzjar.pack.gz 版本放在同一目录。当远程 JNLP 调用时,servlet 会自动选择最优版本,提升用户体验并优化网络流量。

5.2. Codebase 动态替换

servlet 还能动态替换 JNLP 中硬编码的 URL。将 <jnlp spec="1.0+" codebase="http://localhost:8080/jnlp-example"> 改为通配符 <jnlp spec="1.0+" codebase="$$context">,servlet 会自动解析为实际值。

支持的通配符包括:

5.3. 添加 Servlet 到类路径

web.xml 中配置 servlet 映射:

<servlet>
    <servlet-name>JnlpDownloadServlet</servlet-name>
    <servlet-class>
        jnlp.sample.servlet.JnlpDownloadServlet
    </servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>JnlpDownloadServlet</servlet-name>
    <url-pattern>*.jar</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>JnlpDownloadServlet</servlet-name>
    <url-pattern>*.jnlp</url-pattern>
</servlet-mapping>

servlet 本身包含在 jardiff.jarjnlp-servlet.jar 中,这些文件位于 Java SDK 下载页面的 Demos & Samples 部分。

在 GitHub 示例中,这些文件位于 java-core-samples-lib 文件夹,并通过 Maven WAR 插件作为 Web 资源引入:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    ...
    <configuration>
        <webResources>
            <resource>
                <directory>
                    ${project.basedir}/java-core-samples-lib/
                </directory>
                <includes>
                    <include>**/*.jar</include>
                </includes>
                <targetPath>WEB-INF/lib</targetPath>
            </resource>
        </webResources>
    </configuration>
</plugin>

6. 总结

Java Web Start 适用于以下场景:

  • 没有应用服务器的(内网)环境
  • 需要操作本地用户文件的应用程序

优势:通过简单的下载协议交付应用,无需额外依赖或配置(除安全要求如 HTTPS、JAR 签名等)。

⚠️ 部署步骤

  1. 从 GitHub 下载代码到安装了 Tomcat 和 Maven 的系统
  2. 在源码目录运行 mvn install
  3. 将生成的 jws.wartarget 目录复制到 Tomcat 的 webapps 文件夹
  4. 启动 Tomcat

默认情况下,示例可通过 http://localhost:8080/jws/index.html 访问。


原始标题:A Guide to Java Web Start | Baeldung