You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

jPOS GenericPackager在JAR运行时抛空指针异常,IDE中正常(含根因与解决方案)

jPOS GenericPackager JAR运行时抛出NullPointerException问题分析与解决

根因分析

问题核心在于线程上下文类加载器差异:

  • CompletableFuture.runAsync(Runnable)默认使用ForkJoinPool.commonPool(),该线程池的线程上下文类加载器与Spring Boot应用主线程的类加载器不一致。
  • JAR包运行环境中,类资源被打包在JAR内部,GenericPackager初始化过程依赖线程上下文类加载器访问内部资源,使用ForkJoinPool线程时无法正确加载资源,进而触发NullPointerException。
  • IDE运行时类资源以散列文件形式存在,类加载器环境更宽松,因此不会触发该问题。

解决方案

替换默认异步执行方式,指定自定义线程池,确保异步线程的上下文类加载器继承主线程的类加载器:
修改createCompletableFutureRun方法,将无参数的runAsync替换为带自定义线程池的版本:

private CompletableFuture<Void> createCompletableFutureRun(Runnable runnable) {
    // 使用自定义线程池替代默认ForkJoinPool
    return CompletableFuture.runAsync(runnable, Executors.newFixedThreadPool(1));
}

注:生产环境建议使用Spring管理的线程池(如ThreadPoolTaskExecutor),避免临时创建线程池导致的资源泄漏问题。


原问题信息

问题复现步骤

  • 执行mvn clean package生成JAR包
  • 在IDE中运行IsoPackagerExceptionApplication类,运行正常
  • 使用java -jar target/ISOPackagerException-0.0.1-SNAPSHOT.jar运行JAR包,抛出异常
  • 通过Postman或cURL发起请求:curl --location --request POST 'http://localhost:8123/test'

JAR运行时异常栈信息

org.jpos.iso.ISOException: java.lang.NullPointerException
        at org.jpos.iso.packager.GenericPackager.readFile(GenericPackager.java:224) ~[jpos-2.1.8.jar!/:2.1.8]
        at org.jpos.iso.packager.GenericPackager.<init>(GenericPackager.java:126) ~[jpos-2.1.8.jar!/:2.1.8]
        at com.example.ISOPackagerException.controller.Controller.lambda$testMethod$0(Controller.java:52) ~[classes!/:0.0.1-SNAPSHOT]

核心代码

package com.example.ISOPackagerException.controller;

import org.jpos.iso.packager.GenericPackager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import java.io.InputStream;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;

@RestController
@RequestMapping(value = "/test")
public class Controller {

    private static final Logger logger = LoggerFactory.getLogger(Controller.class);

    @PostMapping
    @ResponseStatus(HttpStatus.OK)
    public void test() {
        testMethod();
    }

    public void testMethod() {
        logger.info("Testing GenericPackager1 (This works fine)");
        try {
            InputStream inputStream = getClass().getResourceAsStream("/some.xml");
            if (inputStream == null) {
                logger.error("GenericPackager1 inputStream is null");
            } else {
                logger.info("GenericPackager1 inputStream is NOT null");
                GenericPackager genericPackager = new GenericPackager(inputStream);
                logger.info("Successful GenericPackager1: " + genericPackager);
            }
        } catch (Exception e) {
            logger.error("GenericPackager1 error", e);
        }

        createCompletableFutureRun(() -> {
            logger.info("Testing GenericPackager2 in CompletableFuture (This throws the NPE when running from JAR)");
            try {
                InputStream inputStream = getClass().getResourceAsStream("/some.xml");
                if (inputStream == null) {
                    logger.error("GenericPackager2 inputStream is null");
                } else {
                    logger.info("GenericPackager2 inputStream is NOT null");
                    GenericPackager genericPackager = new GenericPackager(inputStream);
                    logger.info("Successful GenericPackager2 in CompletableFuture: " + genericPackager);
                }
            } catch (Exception e) {
                logger.error("GenericPackager2 error", e);
            }
        });
    }

    private CompletableFuture<Void> createCompletableFutureRun(Runnable runnable) {
        return CompletableFuture.runAsync(runnable);
        // 解决方案
        // return CompletableFuture.runAsync(runnable, Executors.newFixedThreadPool(1));
    }
}

内容的提问来源于stack exchange,提问作者Alarka Sanyal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 00:00:11