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 加载异常。
- 如果独立程序能看到
jarProvider,仍需检查 Liferay 进程中的实际加载环境;独立程序成功不能证明 JSP 编译环境一定正常。
--add-modules=jdk.zipfs 只能在模块已存在时作为条件性排查手段。它不能补回运行时中已经被裁掉的模块,也不应在缺少证据时当作固定修复参数。
4. 用最小 JSP 缩小故障范围
可以临时将视图简化为:
<%@ page contentType="text/html; charset=UTF-8" %>
<p>JSP compilation check</p>
重新部署后分步恢复:
- 恢复
init.jsp和标签库声明。 - 恢复使用静态数据的
search-container。 - 恢复
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 的完整堆栈。
备注:内容仅供参考。
