alibaba / fastjson2

🚄 FASTJSON2 is a Java JSON library with excellent performance.
Apache License 2.0
3.79k stars 495 forks source link

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

Open rootshk opened 1 year ago

rootshk commented 1 year ago

问题描述

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

环境信息

重现步骤

  1. 调用方法

    JSON.parseArray(data).toList(PD01.class);
  2. 输入参数

    // 无
  3. 出现 com.alibaba.fastjson2.JSONException: create instance error 错误

  4. 复现代码

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>对象,并操作

### 相关日志输出
```log
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 java.base@17.0.7/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 java.base@17.0.7/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>

2. 项目以Spring Native方式打包

mvn clean native:compile -Pnative -DskipTests=true -T 10

target/test_a

rootshk commented 1 year ago

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

rootshk commented 1 year ago

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

rootshk commented 1 year ago

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

wenshao commented 1 year ago

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

niaoshuai commented 1 year ago

咋修复的

rootshk commented 1 year ago

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

imgoby commented 10 months ago

the same.request answer

wssy001 commented 7 months ago

@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)
        ;
    }
}
LiuC123 commented 6 months ago

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