Orika、CompletableFuture结合使用:报MappingException-java.lang.NullPointerException

1.背景

项目需要使用到一个实体映射工具(Entity Mapping)。例如:实体A、B,字段名称全部一样,将实体A中的字段值,全部复制到实体B中。
调查过程中,采用orika-mapper框架,本地单元测试也表现良好。

2.问题

当将使用orika代码,在测试环境运行,转换过程报NullPointerException。详细异常堆栈信息见下方

java.lang.RuntimeException: ma.glasnost.orika.MappingException: While attempting the following mapping:
sourceType = com.ctrip.corp.hotelbooking.bookcommon.db.model.corporderdb.CorpOrders
destinationType = com.ctrip.corp.hotelbooking.bookcommon.db.entity.corporderdb.HTLCCorpOrders
Error occurred: java.lang.NullPointerException
-------------------------------------------------------------
Unenhance strategy: ma.glasnost.orika.unenhance.BaseUnenhancer@1e408a3b
-----end dump of current state-------------------------------
    at ma.glasnost.orika.impl.generator.MapperGenerator.build(MapperGenerator.java:105)
    at ma.glasnost.orika.impl.DefaultMapperFactory.buildMapper(DefaultMapperFactory.java:1372)
    at ma.glasnost.orika.impl.DefaultMapperFactory.lookupMapper(DefaultMapperFactory.java:757)
    at ma.glasnost.orika.impl.DefaultMapperFactory.lookupMapper(DefaultMapperFactory.java:707)
    at ma.glasnost.orika.impl.MapperFacadeImpl.resolveMapper(MapperFacadeImpl.java:591)
    at ma.glasnost.orika.impl.MapperFacadeImpl.resolveMappingStrategy(MapperFacadeImpl.java:216)
    at ma.glasnost.orika.impl.MapperFacadeImpl.map(MapperFacadeImpl.java:741)
    at ma.glasnost.orika.impl.MapperFacadeImpl.map(MapperFacadeImpl.java:721)
    at com.ctrip.corp.hotelbooking.bookcommon.db.repository.entityMapper.EntityMapper.map(EntityMapper.java:26)
    at com.ctrip.corp.hotelbooking.bookcommon.db.repository.impl.CorpOrdersRepositoryImpl.insert(CorpOrdersRepositoryImpl.java:27)
    at com.ctrip.corp.hotelbooking.hotelws.manager.createorder.member.sub.SaveHTLCCorpOrdersBiz.saveHTLCCorpOrder(SaveHTLCCorpOrdersBiz.java:95)
    at com.ctrip.corp.hotelbooking.hotelws.manager.createorder.member.sub.SaveCorpOrderDBBiz.lambda$saveCorpOrderDBAsync$4(SaveCorpOrderDBBiz.java:92)
    at com.ctrip.corp.hotelbooking.bookcommon.utility.util.LambdaUtil.lambda$tryOf$1(LambdaUtil.java:46)
    ... 6 more
Caused by: java.lang.NullPointerException
    at ma.glasnost.orika.impl.generator.JavassistCompilerStrategy.registerClassLoader(JavassistCompilerStrategy.java:133)
    at ma.glasnost.orika.impl.generator.JavassistCompilerStrategy.compileClass(JavassistCompilerStrategy.java:235)
    at ma.glasnost.orika.impl.generator.SourceCodeContext.compileClass(SourceCodeContext.java:249)
    at ma.glasnost.orika.impl.generator.SourceCodeContext.getInstance(SourceCodeContext.java:266)
    at ma.glasnost.orika.impl.generator.MapperGenerator.build(MapperGenerator.java:75)
    ... 18 more

后续通过源码调查发现,是因为使用orika框架映射实体的代码,在jdk1.8---CompletableFuture内部执行,CompletableFuture内部维护的线程池-ForkJoinPool中的Thread,通过方法getContextClassLoader()获取类加载器上下文环境为 NULL

Orika、CompletableFuture结合使用:报MappingException-java.lang.NullPointerException_第1张图片
异常核心代码

简单解决方案:
为CompletableFuture传入一个自定义的ForkJoinPool,这样线程池中Thread.getContextClassLoader()非空。

搜集知识:
1.JAVA_OPTS 的作用:是用来设置JVM相关运行参数的变量。
eg:设置tomcat->Edit Configuration-> VM options。JVM:JAVA_OPTS="-server -Xms2048m -Xmx2048m -Xss512k"

1.JDK有ClassLoader、Tomcat也有ClassLoader的(JavaWeb强相关);

2.不同的ClassLoader是可以配置参数的。
eg:AppClassLoader可以通过 -classpath 配置类加载路径。

3.当前的加载类的classpath路径信息,可用以下命令进行查看:

this.getClass().getClassLoader().getResource("").toString();

控制台验证截图:

tomcat中线程名:http-nio-8899-exec-4
tomcat中线程的classpath:file:/D:/Users/hd.jin/gitProject/hotelws-java/hotelws-service/target/hotelws/WEB-INF/classes/
CompletableFuture内线程池中Thread的名字ForkJoinPool.commonPool-worker-1
java.util.concurrent.CompletionException: java.lang.RuntimeException: java.lang.NullPointerException
Caused by: java.lang.NullPointerException
    at com.ctrip.corp.hotelbooking.hotelws.service.impl.CorpHotelBookServiceImpl.lambda$sendSMS$0(CorpHotelBookServiceImpl.java:166)
    at com.ctrip.corp.hotelbooking.bookcommon.utility.util.LambdaUtil.lambda$tryOf$1(LambdaUtil.java:46)
    ... 6 more
java.lang.RuntimeException: java.lang.NullPointerException

思考:显式为ForkJoinPool.commonPool设置:setContextClassLoader是可行的。

4.Tomcat容器如果分发请求、如何处理多用户同时请求?
答:分发请求时Connector组件处理;处理请求的细节是Container来处理。
男主外沟通,女主内处理请求。

5.多线程下 Thread.currentThread().getContextClassLoader()
线程池中线程创建,类加载上下文的设置contextClassLoader VS Tomcat中线程contextClassLoader设置,核心不同之处。

3.文章参考

1.NullPointerException in JavassistCompilerStrategy.registerClassLoader when using ForkJoinPool #225
2.Parallel stream doesn't set Thread.contextClassLoader after tomcat upgrade

你可能感兴趣的:(Orika、CompletableFuture结合使用:报MappingException-java.lang.NullPointerException)