PAYATHON 2026

Liferay 7.4 MVC portlet 使用 search-container 时 JSP 编译失败如何排查?

数据小许

结论

建议先排查服务器使用的 JDK 和 JSP 编译时的文件系统 Provider。日志显示,异常发生在 javac 编译 JSP 时,但还无法确定缺失的是哪个 Provider,也无法认定 search-container 的写法是根因。

jdk.zipfs 值得检查,它提供 ZIP/JAR 文件系统支持。如果编译器访问 JAR 时需要该 Provider,而运行环境未包含或无法加载它,就可能出现相关异常。当前日志没有给出 ProviderNotFoundException 自身的完整调用栈,不能断言安装或启用该模块一定能解决问题。

1. 找到最内层异常的完整堆栈

当前异常链是:

JSP 编译失败
  → javac 调用失败
    → java.nio.file.ProviderNotFoundException: Provider not found

继续查看同一次请求后续的日志,找到直接以以下内容开头的异常段及其调用栈:

Caused by: java.nio.file.ProviderNotFoundException: Provider not found

重点确认异常由哪个文件系统 API 抛出,是否涉及 JAR、ZIP、文件路径或 URI。缺少这段堆栈时,只能列出排查方向,无法确定具体修复办法。

2. 确认 Liferay 实际使用的 JDK

先从服务器启动配置和进程信息中确认 Java 可执行文件路径,再用同一套 JDK 检查:

/path/to/liferay-jdk/bin/java -version
/path/to/liferay-jdk/bin/java --list-modules

将示例路径替换为实际路径。--list-modules 适用于 Java 9 及以上版本。

检查模块列表中是否包含:

jdk.compiler
jdk.zipfs

日志中已经出现 jdk.compiler,说明编译器被调用过,但这不能证明其他所需模块也完整可用。还要留意服务器是否使用了经过裁剪的 Java 运行时。

同时核对该 JDK 是否适用于当前 Liferay 的具体更新版本。仅凭“Liferay 7.4”无法确定支持的 Java 版本,也不宜直接换成任意最新 JDK。

3. 检查 ZIP/JAR Provider 是否可用

如需进一步检查,可以使用服务器同一套 JDK 编译并运行以下诊断程序:

import java.nio.file.spi.FileSystemProvider;

public class ListProviders {
    public static void main(String[] args) {
        System.out.println("java.home=" + System.getProperty("java.home"));

        for (FileSystemProvider provider :
                FileSystemProvider.installedProviders()) {
            System.out.println(
                provider.getScheme() + " -> " +
                provider.getClass().getName());
        }
    }
}
/path/to/liferay-jdk/bin/javac ListProviders.java
/path/to/liferay-jdk/bin/java ListProviders

查看是否存在 scheme 为 jar 的 Provider,并结合完整异常堆栈判断。

  • 如果运行时缺少 jdk.zipfs,且堆栈证实问题涉及 ZIP/JAR 文件系统,应使用该 Liferay 版本支持的完整 JDK,或修正裁剪运行时的模块配置。
  • 如果模块存在但 Provider 无法加载,应继续检查启动参数、模块限制及 Provider 加载异常。
  • 如果独立程序能看到 jar Provider,仍需检查 Liferay 进程中的实际加载环境;独立程序成功不能证明 JSP 编译环境一定正常。

--add-modules=jdk.zipfs 只能在模块已存在时作为条件性排查手段。它不能补回运行时中已经被裁掉的模块,也不应在缺少证据时当作固定修复参数。

4. 用最小 JSP 缩小故障范围

可以临时将视图简化为:

<%@ page contentType="text/html; charset=UTF-8" %>
<p>JSP compilation check</p>

重新部署后分步恢复:

  1. 恢复 init.jsp 和标签库声明。
  2. 恢复使用静态数据的 search-container。
  3. 恢复 OwnerLocalServiceUtil 调用和各列属性。

如果最小 JSP 也报同样的 Provider 异常,应优先处理编译环境。如果只在加入标签库或业务 API 后失败,就沿着新增部分涉及的依赖和 JAR 继续排查。某段代码触发了问题,并不代表它的语法本身有错。

5. 环境问题解决后再检查业务依赖

现有 Gradle 配置中的 compileOnly 不会将对应依赖打包进 Web 模块。还需确认 Service Builder 的 API 和 service 模块已经部署、所需包能够解析,且 OwnerLocalService 已注册。

业务类缺失、服务不可用和文件系统 Provider 异常是不同的线索。现有日志不足以支持直接删除 com.liferay.util.taglib 依赖,也不足以支持把 compileOnly 全部改成 implementation。

如果后续日志出现明确的类无法解析、服务不可用或属性不存在错误,再分别核对模块状态、生成的 API,以及 Owner 模型是否具有与 JSP 中各个 property 对应的 getter。

要进一步确定根因,需要补充 Liferay 的具体更新版本、服务器实际使用的 JDK 版本和路径,以及最内层 ProviderNotFoundException 的完整堆栈。

备注:内容仅供参考。