fastjson2 icon indicating copy to clipboard operation
fastjson2 copied to clipboard

[BUG]Spring Boot 3在GraalVM Native打包序列化异常

Open rootshk opened this issue 1 year ago • 9 comments

问题描述

在GraalVM CE情况下使用JSONPath获取出现一下一场 com.alibaba.fastjson2.JSONException: create instance error

环境信息

  • OS信息: [e.g.:macOS13.4.1 (c) (22F770820d) 16 GB]
  • JDK信息: [e.g.:GraalVM CE 17.0.7]
  • 版本信息:[e.g.:Fastjson2 2.0.37]

重现步骤

  1. 调用方法
JSON.parseArray(data).toList(PD01.class);
  1. 输入参数
// 无
  1. 出现 com.alibaba.fastjson2.JSONException: create instance error 错误

  2. 复现代码



public class X {

    public static void nativeTest() {
        String data = "[{\"PD01A\":{\"PD01AD03\":2,\"PD01AD01\":1}}}]";
        List<PD01> l = JSON.parseArray(data).toList(PD01.class);
    }

    public static void main(String[] args) {
        nativeTest();
    }
    public static class PD01 {
        private PD01A PD01A;

        public PD01() {}
        public PD01(PD01A PD01A) { this.PD01A = PD01A; }

        public PD01A getPD01A() { return PD01A; }
        public void setPD01A(PD01A PD01A) { this.PD01A = PD01A; }
    }

    public static class PD01A {
        private String PD01AD01;
        private String PD01AD03;

        public PD01A() {}
        public PD01A(String PD01AD01, String PD01AD03) {
            this.PD01AD01 = PD01AD01;
            this.PD01AD03 = PD01AD03;
        }

        public String getPD01AD01() { return PD01AD01; }
        public void setPD01AD01(String PD01AD01) { this.PD01AD01 = PD01AD01; }
        public String getPD01AD03() { return PD01AD03; }
        public void setPD01AD03(String PD01AD03) { this.PD01AD03 = PD01AD03; }
    }

}

期待的正确结果

能正确获得List<PD01>对象,并操作

相关日志输出

2023-07-27T15:57:34.115+08:00 ERROR 97705 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: com.alibaba.fastjson2.JSONException: create instance error, class com.example.test_a.ttt.PD01] with root cause

com.alibaba.fastjson2.JSONException: create instance error, class com.example.test_a.ttt.PD01
        at com.alibaba.fastjson2.reader.ObjectReaderAdapter.createInstance0(ObjectReaderAdapter.java:333) ~[test_a:na]
        at com.alibaba.fastjson2.reader.ObjectReaderAdapter.createInstance(ObjectReaderAdapter.java:382) ~[test_a:na]
        at com.alibaba.fastjson2.reader.ObjectReaderBean.readObject(ObjectReaderBean.java:332) ~[test_a:na]
        at com.alibaba.fastjson2.reader.ObjectReaderImplList.readObject(ObjectReaderImplList.java:570) ~[na:na]
        at com.alibaba.fastjson2.JSONReader.readArray(JSONReader.java:2151) ~[test_a:na]
        at com.alibaba.fastjson2.JSON.parseArray(JSON.java:2469) ~[na:na]
        at com.example.test_a.TestAApplication.index(TestAApplication.java:58) ~[test_a:na]
        at [email protected]/java.lang.reflect.Method.invoke(Method.java:568) ~[test_a:na]
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:207) ~[test_a:6.0.10]
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:152) ~[test_a:6.0.10]
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[test_a:6.0.10]
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884) ~[test_a:6.0.10]
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) ~[test_a:6.0.10]
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[test_a:6.0.10]
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1081) ~[test_a:6.0.10]
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974) ~[test_a:6.0.10]
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011) ~[test_a:6.0.10]
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) ~[test_a:6.0.10]
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) ~[test_a:6.0]
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[test_a:6.0.10]
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[test_a:6.0]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) ~[na:na]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[na:na]
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[test_a:10.1.10]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[na:na]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[na:na]
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[test_a:6.0.10]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[test_a:6.0.10]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[na:na]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[na:na]
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[test_a:6.0.10]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[test_a:6.0.10]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[na:na]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[na:na]
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[test_a:6.0.10]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[test_a:6.0.10]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[na:na]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[na:na]
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:166) ~[na:na]
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[na:na]
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[test_a:10.1.10]
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[na:na]
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[test_a:10.1.10]
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[na:na]
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341) ~[na:na]
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[na:na]
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[test_a:10.1.10]
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:894) ~[na:na]
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1741) ~[na:na]
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[test_a:10.1.10]
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[na:na]
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[na:na]
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[na:na]
        at [email protected]/java.lang.Thread.run(Thread.java:833) ~[test_a:na]
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:807) ~[test_a:na]
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:210) ~[na:na]

附加信息

  1. 项目POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.1.1</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>test_a</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>test_a</name>
	<description>test_a</description>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.12.0</version>
		</dependency>
		<dependency>
			<groupId>com.alibaba.fastjson2</groupId>
			<artifactId>fastjson2</artifactId>
			<version>2.0.37</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.json</groupId>
			<artifactId>json</artifactId>
			<version>20230618</version>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.graalvm.buildtools</groupId>
				<artifactId>native-maven-plugin</artifactId>
			</plugin>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>
  1. 项目以Spring Native方式打包
# mvn clean native:compile -Pnative -DskipTests=true -T 10
# target/test_a

rootshk avatar Jul 27 '23 08:07 rootshk

以上代码在jar包模式下运行无异常,只在GraalVM方式下有问题

rootshk avatar Jul 27 '23 08:07 rootshk

纠正代码中错误 改为: String data = "[{"PD01A":{"PD01AD03":2,"PD01AD01":1}}]";

rootshk avatar Jul 27 '23 08:07 rootshk

使用GraalVM 17.0.8 和GraalVM CE 17.0.8 问题是相同的

rootshk avatar Jul 27 '23 09:07 rootshk

收到反馈,这个要晚点看,如果可以,也请你帮忙进来分析下原因。

wenshao avatar Jul 30 '23 04:07 wenshao

咋修复的

niaoshuai avatar Oct 18 '23 09:10 niaoshuai

我也没解决,换回jar包模式了

rootshk avatar Oct 18 '23 09:10 rootshk

the same.request answer

imgoby avatar Jan 19 '24 06:01 imgoby

@rootshk @imgoby @niaoshuai

FastJSON2版本:2.0.46

解决方案1:

1.

import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.RuntimeSerialization;

public class NativeConfig implements Feature {
    @Override
    public void beforeAnalysis(BeforeAnalysisAccess access) {
        // PD01  PD01A需要实现 java.io.Serializable
        RuntimeSerialization.register(X.PD01.class);
        RuntimeSerialization.register(X.PD01A.class);
    }
}

2. 然后Native Image编译命令中添加 --features=xxx.xxx.NativeConfig 即可


解决方案2:SpringBoot 3.x

1.

import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportRuntimeHints;

@Configuration
@ImportRuntimeHints(NativeConfig.class)
public class NativeConfig implements RuntimeHintsRegistrar {
    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
        // PD01  PD01A需要实现 java.io.Serializable
        hints.serialization()
                .registerType(X.PD01.class)
                .registerType(X.PD01A.class)
        ;
    }
}

wssy001 avatar Apr 02 '24 05:04 wssy001

@wssy001 按照你的方式,成功解決。非常感謝你的分享

LiuC123 avatar May 11 '24 02:05 LiuC123