兼容性与保护范围

P4JX 会加密保存受保护类的方法实现,并且只允许随包的 VLX JRE 在运行时读取。为了在保护强度和框架兼容性之间取得平衡,建议只保护核心业务实现,把框架入口、DTO 与实体、需要由框架在运行时动态生成或修改的类,以及第三方依赖排除在保护范围之外。

1. 先运行兼容性扫描

只扫描,不产生输出:

p4j javaapp app.jar --compat-scan
p4j springboot app.jar --compat-scan
p4j tomcat app.war --compat-scan

扫描并应用保守建议:

p4j springboot app.jar dist --compat-apply

你显式指定的选项,优先级始终高于自动建议。扫描属于静态启发式分析:报告中出现「需要复核」,不代表应用一定会出问题;报告没有发现问题,也不能替代在目标平台上的回归测试。

扫描结果要求修改代码时

如果 GUI 报告显示需要操作(英文界面为 ACTION REQUIRED),说明必须修改应用源码,这类问题无法通过调整打包参数解决。报告会列出涉及的具体类和方法,并给出以下一类处理建议:

  • 从内存读取 ZIP 或 JAR:把 ByteArrayInputStream 改成基于文件的读取方式,例如 Files.newInputStream(Path)FileInputStreamZipFileJarFile
  • 从原始字节定义类:不要对受保护的类直接调用 ClassLoader#defineClass,改用 Class.forName()ClassLoader.loadClass(),让 P4JX 运行时完成类加载。

修改源码后重新构建 JAR 或 WAR,再执行一次兼容性扫描;确认报告中不再出现该问题,再生成保护包。

2. 推荐的保护模型

公开边界/框架入口 → 普通 facade 或接口 → 受保护的核心实现

示例:

--protect 'com.example.service.impl.**' \
--exclude 'com.example.dto.**,com.example.config.**'

3. 通常不应保护的类

  • 控制器、Servlet、Filter、Listener、Advice;
  • Spring 配置类,以及经 AOT 或 CGLIB 增强的对象;
  • Jackson DTO、record、JPA 实体、序列化模型;
  • JNI、SWT 等原生桥接类;
  • 由 Agent、ORM、Mock 框架、热重载或自定义 ClassLoader 改写或重新定义的类;
  • 第三方框架和开源依赖;
  • 需要读取自身真实 class 字节码的类。

4. 不支持或受限的 JVM 能力

运行环境

  • 受保护应用必须使用随包的 VLX JRE 运行;
  • 不支持 JPMS 的 -m 启动方式,也不支持单文件源码启动;
  • 类路径上的普通 JAR 必须由打包器登记,不能在部署时随意追加;
  • 不要用 VLX JRE 去运行未受保护的普通应用。

调试与 Agent

  • 不支持 JVMTI、JDWP 调试器、性能分析器、覆盖率工具以及大多数 APM agent;
  • 不支持 -javaagent-agentlib、热重载和类重定义;
  • 需要字节码织入的处理,必须在编码之前完成。

启动优化

  • 不支持 CDS、AppCDS 和 AOT;
  • ZGC 与 Shenandoah GC 不在 P4JX 运行时的支持范围内。

5. 类资源与扫描器

读取受保护类的 .class 资源时,得到的是元数据桩:类名、签名和注解都保留,但不含真实的方法体。

  • 普通反射可以读到元数据,但无法还原原始字节码;
  • 自行解析物理 ZIP 的工具默认看不到 P4JX 归档中的内容,可以尝试 --zip-overlay scanner
  • ClassGraph、Reflections 这类类路径扫描器,在 Spring Boot 下可能需要配合 --layout fat
  • 不支持用 ZipInputStreamJarInputStream 从内存流、网络流中解析 P4JX 归档;
  • zipfs 视图是只读的,无法通过它修改归档。

6. 归档行为

  • .jar 后缀并不代表它是普通 JAR,内容仍然是 P4JX;
  • Multi-Release JAR 会在编码时按目标 Java 版本展平;
  • 原 JAR 的签名和证书语义不会保留,如果分发环节有要求,请对最终交付物单独签名;
  • 不要修改、重新压缩或合并生成的 P4JX 文件。