兼容性与保护范围
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)、FileInputStream、ZipFile或JarFile。 - 从原始字节定义类:不要对受保护的类直接调用
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; - 不支持用
ZipInputStream或JarInputStream从内存流、网络流中解析 P4JX 归档; - zipfs 视图是只读的,无法通过它修改归档。
6. 归档行为
.jar后缀并不代表它是普通 JAR,内容仍然是 P4JX;- Multi-Release JAR 会在编码时按目标 Java 版本展平;
- 原 JAR 的签名和证书语义不会保留,如果分发环节有要求,请对最终交付物单独签名;
- 不要修改、重新压缩或合并生成的 P4JX 文件。