JDK1.8升级 JDK21 实践踩坑

一、JDK21优势

将 JDK 从 1.8(Java 8)升级到 JDK 21(LTS 版本)可以带来多方面的收益,包括性能提升、语言特性增强、开发效率提高、安全性改进等。

1.1 性能提升

  • ZGC 和 Shenandoah 垃圾回收器: JDK 21 默认支持低延迟垃圾回收器,显著减少 GC 停顿时间(可控制在毫秒级),适合高吞吐量和低延迟应用。
  • ZGC:支持 TB 级堆内存,停顿时间不超过 10ms。
  • Shenandoah:并发回收,减少停顿时间。
  • 虚拟线程(轻量级线程): JDK 21 正式引入虚拟线程(Project Loom),显著提升高并发应用的吞吐量,减少线程创建和上下文切换的开销。
  • 向量化计算(Vector API): 支持 SIMD 指令(JDK 16 引入,JDK 21 进一步优化),加速数值计算和机器学习任务。

1.2 语言特性增强

  • 模式匹配(Pattern Matching)

  • instanceof 模式匹配(JDK 16):简化类型检查和转换,合并类型检查和类型转换:if (obj instanceof String s) { s.length(); }

  • switch 表达式和模式匹配(JDK 17~21):支持更复杂的条件分支逻辑。

  • 记录模式(Record Patterns,JDK 21):直接解构记录类(Record)的数据。

  • 文本块(Text Blocks) JDK 15 引入,简化多行字符串的编写(如 JSON、SQL 等)。

1.3 开发效率提升

  • 标准化 HTTP Client(JDK 11 取代 HttpURLConnection)。
  • 增强的 Stream API(如 Collectors.teeing()、takeWhile()、dropWhile()、ofNullable())。
  • 快速创建不可变集合:List.of("a", "b")、Map.of("k", 1)
  • 文本块:多行字符串无需转义

1.4 容器化支持

  • 改进的 Docker 和 Kubernetes 兼容性

  • JDK 10+ 支持容器感知的 CPU 和内存资源限制(自动识别容器环境而非宿主机)。

  • 减少 JVM 在容器中的内存占用问题。

  • Native Image(GraalVM 集成) 通过 GraalVM 将 Java 应用编译为本地镜像(需额外工具支持),减少启动时间和内存占用。

1.5 长期支持(LTS)

  • JDK 21 是 LTS 版本,提供长期更新支持(至少到 2031 年),适合生产环境。
版本 特性亮点
JDK 9 模块化、集合工厂方法、JShell、Try-With-Resources 增强
JDK 10 var局部变量类型推断
JDK 11 HTTP Client 标准化、ZGC、字符串 API 增强(strip()、repeat())
JDK 12-15 Switch 表达式、文本块、instanceof模式匹配(预览→正式)
JDK 16-17 记录类、密封类(正式化)
JDK 21 虚拟线程、结构化并发、模式匹配 Switch(正式)

二、环境配置

2.1 下载安装JDK21

  1. MacOS下载

www.oracle.com/java/techno...

  1. Windows下载

www.oracle.com/java/techno...

2.2 升级IDEA

JDK21只有2023.2.2及以上版本IDEA才能支持,没有升级到最新版本的同学,先升级IDEA

参照 JDK21升级指南 的 "2.3 本地启动"

2.3 本地启动

File->Project Structure 检查设置

File->Settings 检查设置

启动配置

  1. 打开启动配置,点击Modify options
  1. 点击Add VM options
  1. 新增JVM参数
css 复制代码
--add-opens=java.base/java.time=ALL-UNNAMED
--add-opens=java.base/java.lang=ALL-UNNAMED 
--add-opens=java.base/java.util=ALL-UNNAMED 
--add-opens=java.base/java.math=ALL-UNNAMED 
--add-opens=java.base/sun.reflect=ALL-UNNAMED 
--add-opens=com.alibaba/spring.context=ALL-UNNAMED
--add-exports=java.base/sun.reflect.annotation=ALL-UNNAMED 
--add-exports=java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED
  1. 点击运行,起来了!

2.4 ZGC使用

在 JDK 21 中,分代 ZGC 是默认的 ZGC 模式(无需额外配置)。但如果需要显式启用或验证,可以使用以下 JVM 参数:java -XX:+UseZGC -XX:+ZGenerational ...

  • -XX:+UseZGC:启用 ZGC。
  • -XX:+ZGenerational:启用分代模式(JDK 21 默认开启,可省略)。
  • 如果需要禁用分代模式(回退到非分代 ZGC),使用 -XX:-ZGenerational。

分代 ZGC 会自动调整年轻代和老年代比例,但可通过以下参数干预:

年轻代大小:-XX:ZYoungGenerationSize=<size 例如 -XX:ZYoungGenerationSize=2g

  • 年轻代默认占堆的 10%~30%,根据应用特性调整(短生命周期对象多的应用可适当增大)。

并发 GC 线程数:-XX:ConcGCThreads=<threads 并发 GC 线程数(默认自动计算)

  • 通常无需手动设置,ZGC 会根据 CPU 核心数自动分配。

最大停顿时间目标:-XX:MaxGCPauseMillis=<ms 例如 -XX:MaxGCPauseMillis=10

  • ZGC 的设计目标是停顿时间不超过 10ms,此参数用于提示 GC 尽量满足要求。

大对象分配:-XX:ZAllocationSpikeTolerance=<factor 默认 2.0,控制分配速率的敏感度

  • 适用于突发内存分配的场景。

NUMA 感知(多核服务器):-XX:+UseNUMA 启用 NUMA 内存分配优化

GC 日志输出:-XX:+PrintGCDetails -Xlog:gc*,gc+stats=info:file=gc.log:time,uptime:filecount=5,filesize=100m

  • 输出详细的 GC 日志和统计信息,便于分析停顿时间和内存回收效率。

使用 jstat 查看内存和 GC 状态:jstat -gcutil <pid 1s 每秒输出一次 GC 统计

  • 通过 JMX 工具(如 VisualVM 或 JConsole)监控堆内存和 GC 活动。

2.5 虚拟线程使用

创建虚拟线程

  • 方式 1:直接使用 Thread.startVirtualThread(),这是最简单的创建方式,适用于快速测试。

Thread.startVirtualThread(() -> {System.out.println("Hello from virtual thread: " + Thread.currentThread());});

  • 方式 2:使用 Thread.ofVirtual(),可以设置线程名、优先级等属性。

Thread virtualThread = Thread.ofVirtual().name("my-virtual-thread-", 1) // 线程名前缀 + 计数器.start(() -> {System.out.println("Running in virtual thread: " + Thread.currentThread());});

  • 方式 3:使用 Executors.newVirtualThreadPerTaskExecutor(),推荐方式,适用于生产环境,自动管理线程池。

try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {

​ executor.submit(() -> {System.out.println("Task running in virtual thread: " + Thread.currentThread());});}

关键配置参数

  • 虚拟线程是轻量级的,但底层仍依赖 平台线程(Carrier Threads) 来执行任务。可以调整平台线程池大小:-Djdk.virtualThreadScheduler.parallelism=256 # 默认等于 CPU 核心数。
  • 如果任务有较多阻塞操作(如 I/O),可以适当增大该值。

调试和监控

  • 查看虚拟线程状态:jcmd <pid Thread.dump_to_file -format=json threads.json
  • 启用虚拟线程调试日志:-Djdk.traceVirtualThreads=true

tomcat线程池改成虚拟线程池

  • springboot 1.x
  • Java @Configuration public class TomcatConfig { @Bean public TomcatServletWebServerFactory servletWebServerFactory() { TomcatServletWebServerFactory tomcatFactory = new TomcatServletWebServerFactory (); tomcatFactory.addConnectorCustomizers(connector -> { if (connector.getProtocolHandler() instanceof Http11NioProtocol) { Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler(); protocol.setExecutor(Executors.newVirtualThreadPerTaskExecutor()); } }); return tomcatFactory; } } - springboot 2.x -XML @Configuration public class TomcatConfig { @Bean public ServletWebServerFactory servletWebServerFactory() { TomcatServletWebServerFactory tomcatFactory = new TomcatServletWebServerFactory(); tomcatFactory.addConnectorCustomizers(connector -> { if (connector.getProtocolHandler() instanceof Http11NioProtocol) { Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler(); protocol.setExecutor(Executors.newVirtualThreadPerTaskExecutor()); } }); return tomcatFactory; } } - springboot3.x 支持配置修改 -Properties spring.threads.virtual.enabled=true

虚拟线程隐藏的坑

1、执行 synchronized 块/方法

  • 原因: synchronized 关键字是 Java 内置的同步机制,其锁操作直接绑定到平台线程(OS 线程)。 若虚拟线程在 synchronized 块内发生阻塞(如 I/O、锁等待),JVM 无法挂起该虚拟线程,因为它需要保持平台线程对监视器锁(Monitor)的持有。

  • synchronized在jvm的实现是依赖于对象的监视器,当方法进入synchronized函数后,jvm将会记录持有对象监视器对应线程。

  • 由于记录的是平台线程,所以如果在虚拟线程A进入对象obj的synchronized函数后,如果没有Pin住Carrier Thread,此时另一个虚拟线程B也被调度到了同样的Carrier Thread上执行对象obj的其他synchronized函数,此时jvm会认为虚拟线程B已经获取了对象的监视器,从而不阻塞直接进入函数内部,导致并发问题。

  • 最极端的情况,如果所有的Carrier Thread都被Pin住,而synchronized外仍然有其他线程与之抢占资源,则会发生死锁。

  • 解决方案:Jdk 21-23:使用JUC中的锁(比如ReentrantLock)来替换掉synchronized。包括外部依赖,比如依赖服务的SDK、使用的平台框架、中间件的SDK等,都需要使用替换掉synchronized的版本。

该问题只存在于jdk 21-23,jdk24对底层synchronized,Object.wait()以及notify()函数进行了大量的重写,经过jdk 开发团队的努力,synchronized已经不会导致Carrier Thread被Pin 了。JEP 491: Synchronize Virtual Threads without Pinning

2、调用 Native 方法或 JNI 代码

  • 原因:
  • Native 方法(通过 JNI 调用的 C/C++ 代码)可能直接操作平台线程的状态,或执行无法被 JVM 控制的阻塞操作。 JVM 无法感知 Native 方法中的阻塞行为,因此必须固定 Carrier Thread。
  • 和上面的情况类似,如果JNI函数非常的耗时,直接阻塞导致Carrier Thread被用尽,也会发生死锁的问题。

因此使用JNI调用原生方法时,应该尽量避免耗时较长的操作。或者直接单开一个平台线程,不要挤占虚拟线程的资源。

三、依赖升级

  1. spring-boot系列包版本统一为2.7.18
  2. dubbo版本升级到2.7.12-mone-java21-v23-SNAPSHOT(虚拟线程版本)
  3. lombok版本1.18.30
  4. nacos升级到1.4.2-mone-SNAPSHOT
  5. miapi-doc-annos升级到mone-jdk21-SNAPSHOT
  6. dubbo-docs-core升级到mone-jdk21-SNAPSHOT
  7. commons-pool2升级到2.11.1
  8. maven编译插件:
  9. 项目父pom修改
  10. JavaScript org.apache.maven.plugins maven-compiler-plugin 3.11.0 21 21 21 21
  11. AIP模块下的pom文件修改:支持对外提供依赖包通过低版本jdk编译
  12. JavaScript org.apache.maven.plugins maven-compiler-plugin 3.11.0 1.8 1.8 1.8
  13. maven-compiler-plugin里面不能配置${project.basedir}/src/main/java,剔除掉
  14. JavaScript ${project.basedir}/src/main/java
  15. maven-surefire-plugin插件配置
  16. JavaScript org.apache.maven.plugins maven-surefire-plugin 3.1.2 true /backups/ /*Test.java /*Tests.java false true --add-opens=java.base/java.time=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.math=ALL-UNNAMED --add-opens=java.base/sun.reflect=ALL-UNNAMED --add-opens=com.alibaba/spring.context=ALL-UNNAMED --add-exports=java.base/sun.reflect.annotation=ALL-UNNAMED --add-exports=java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED true org.junit.jupiter junit-jupiter-engine 5.8.2

四、遇到的问题

启动:必须添加VM options !!

在 Java 9 中,引入了模块化系统(Jigsaw),java.base 模块默认不会将其内部的包(如 java.util)开放给未命名模块。其中每个模块都有自己的命名空间,可以控制对其公共 API 的访问。模块可以使用 exports 关键字来导出其公共 API,其他模块可以使用 requires 关键字来声明对该模块的依赖关系。这样可以确保模块之间的隔离性和安全性。

然而,有时候我们需要在模块之间共享一些非公共的 API,这时就需要使用 --add-opens 和 --add-exports 命令行选项来解决访问限制问题。

--add-opens:命令行选项可以打开一个模块的某个包,使得其他模块可以访问该包中的非公共 API

--add-exports:命令行选项可以导出一个模块的某个包,使得其他模块可以访问该包中的公共 API

所以每一个单测和应用 Application 都要加下面的 VM Options

css 复制代码
--add-opens=java.base/java.time=ALL-UNNAMED
--add-opens=java.base/java.lang=ALL-UNNAMED 
--add-opens=java.base/java.util=ALL-UNNAMED 
--add-opens=java.base/java.math=ALL-UNNAMED 
--add-opens=java.base/sun.reflect=ALL-UNNAMED 
--add-exports=java.base/sun.reflect.annotation=ALL-UNNAMED 
--add-exports=java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED
--add-opens=com.alibaba/spring.context=ALL-UNNAMED

IDEA编译/启动报错:java: Cannot find JDK '21' for module 'car-service-equity-api'

解决:由于项目中不同模块用了不同的 JDK 版本,推荐勾选上 "Delegate IDE build/run actions to Maven"

img

另外,由于要求 api 模块要用 jdk1.8 编译,所以需要针对 api 模块专门配置依赖的 SDK

img

img

Nacos报错

kotlin 复制代码
Failed to parse configuration class [com.xiaomi.cnzone.car.equity.server.bootstrap.CarServiceEquityBootstrap]; nested exception is java.lang.annotation.AnnotationFormatError: Invalid default: public abstract com.alibaba.nacos.api.config.ConfigType com.alibaba.nacos.spring.context.annotation.config.NacosPropertySource.type()

原因:com.alibaba.nacos.spring.context.annotation.config.NacosPropertySource注解里面的 type 属性默认值是ConfigType.UNSET(在 nacos-spring-context:1.1.2),引用了 nacos-api 包的 ConfigType 类,这个类在某些版本中没有 UNSET 这个枚举值

解决:升级 nacos-client 包版本到 1.4.2-mone-SNAPSHOT

@EnableDubbo 注解切换

解法:@EnableDubbo 注解等价于{@link DubboComponentScan} and {@link EnableDubboConfig} combination. (不切换也能启动)

Junit5单测启动报错

less 复制代码
Internal Error occurred.
org.junit.platform.commons.JUnitException: TestEngine with ID 'junit-jupiter' failed to discover tests
        at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:160)
        at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverSafely(EngineDiscoveryOrchestrator.java:134)
        at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:108)
        at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:80)
        at org.junit.platform.launcher.core.DefaultLauncher.discover(DefaultLauncher.java:110)
        at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
        at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
        at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
        at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
        at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
        at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
        at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
        at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
        at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: org.junit.platform.commons.JUnitException: ClassSelector [className = 'com.xiaomi.cnzone.car.equity.server.test.SpringReadyTest'] resolution failed
        at org.junit.platform.launcher.listeners.discovery.AbortOnFailureLauncherDiscoveryListener.selectorProcessed(AbortOnFailureLauncherDiscoveryListener.java:39)
        at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolveCompletely(EngineDiscoveryRequestResolution.java:102)
        at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.run(EngineDiscoveryRequestResolution.java:82)
        at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver.resolve(EngineDiscoveryRequestResolver.java:113)
        at org.junit.jupiter.engine.discovery.DiscoverySelectorResolver.resolveSelectors(DiscoverySelectorResolver.java:46)
        at org.junit.jupiter.engine.JupiterTestEngine.discover(JupiterTestEngine.java:69)
        at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:152)
        ... 13 more
Caused by: org.junit.platform.commons.PreconditionViolationException: Could not load class with name: com.xiaomi.cnzone.car.equity.server.test.SpringReadyTest
        at org.junit.platform.engine.discovery.ClassSelector.lambda$getJavaClass$0(ClassSelector.java:75)
        at org.junit.platform.commons.function.Try$Failure.getOrThrow(Try.java:335)
        at org.junit.platform.engine.discovery.ClassSelector.getJavaClass(ClassSelector.java:74)
        at org.junit.jupiter.engine.discovery.ClassSelectorResolver.resolve(ClassSelectorResolver.java:66)
        at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.lambda$resolve$2(EngineDiscoveryRequestResolution.java:134)
        at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
        at java.base/java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1685)
        at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:129)
        at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
        at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:647)
        at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolve(EngineDiscoveryRequestResolution.java:185)
        at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolve(EngineDiscoveryRequestResolution.java:125)
        at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolveCompletely(EngineDiscoveryRequestResolution.java:91)
        ... 18 more
Caused by: java.lang.ClassNotFoundException: com.xiaomi.cnzone.car.equity.server.test.SpringReadyTest
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
        at org.junit.platform.commons.util.ReflectionUtils.lambda$tryToLoadClass$9(ReflectionUtils.java:829)
        at org.junit.platform.commons.function.Try.lambda$call$0(Try.java:57)
        at org.junit.platform.commons.function.Try.of(Try.java:93)
        at org.junit.platform.commons.function.Try.call(Try.java:57)
        at org.junit.platform.commons.util.ReflectionUtils.tryToLoadClass(ReflectionUtils.java:792)
        at org.junit.platform.commons.util.ReflectionUtils.tryToLoadClass(ReflectionUtils.java:748)
        ... 33 more

解法:maven-surefire-plugin插件必须指定 junit 的执行引擎(maven-surefire-plugin插件升级到 3.X 版本后必备)

xml 复制代码
<dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.8.2</version>
        </dependency>

编译报错:Please avoid using the keyword synchronized and use the Lock mechanism for execution

解法:检查代码中是否引入了synchronized 关键字,用Lock 替换

jdk21部署到流水线后,容器里的很多linux命令都不支持

解法:jdk21和jdk8的基础镜像不一样,linux命令安装存在差异。ll 先用ls代替;vim 命令没有,在容器里面执行apt install vim安装一下

运行时异常:Unable to make field private final byte java.time.LocalTime.hour accessible: module java.base does not "opens java.time" to unnamed module

解法:需要加入以下JVM参数,使得java.time可被打开。JDK9参数 对于老项目不是太友好,只有代码跑到需要加参数得位置,才会报错。所以,上线前,得保证每一个接口,每一种场景都测到

--add-opens=java.base/java.time=ALL-UNNAMED

循环引用报错:

vbnet 复制代码
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

解决方案:配置文件中声明允许循环引用-》spring.main.allow-circular-references=true

SpringBoot通过三级缓存可解决循环引用问题

Redis链接问题:Caused by: io.lettuce.core.RedisCommandExecutionException: NOAUTH Authentication required.

Lettuce 6.x版本开始,使用RESP3(Redis 6.x引入)的HELLO命令进行版本自适应判断,但是对于不支持HELLO命令的低版本实例,兼容性存在一定问题。所以对于低版本的实例,建议直接在Lettuce中指定使用RESP2协议(兼容Redis 4/5)的版本来使用。

解决方案:添加一段代码,指定RESP2协议访问Redis即可解决:

kotlin 复制代码
package com.chinaroad.parking.config;

import io.lettuce.core.ClientOptions;
import io.lettuce.core.protocol.ProtocolVersion;
import org.springframework.boot.autoconfigure.data.redis.LettuceClientConfigurationBuilderCustomizer;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;

@Configuration
public class SpringConfig implements LettuceClientConfigurationBuilderCustomizer {
@Override
public void customize(LettuceClientConfiguration.LettuceClientConfigurationBuilder clientConfigurationBuilder) {// manually specifying RESP2 
        clientConfigurationBuilder.clientOptions(ClientOptions.builder()
                .protocolVersion(ProtocolVersion.RESP2)
                .build());
    }
}

使用dubbo发起RPC调用,List类型参数报错

kotlin 复制代码
Caused by: org.apache.dubbo.remoting.RemotingException: Fail to decode request due to: RpcInvocation [methodName=selectStoreByOrgIds, parameterTypes=[class com.xiaomi.cnzone.maindataapi.model.req.store.StoreListByOrgIdsRequest], arguments length=0, attachments={path=com.xiaomi.cnzone.maindataapi.api.StoreProvider, input=617, dubbo=2.0.2, version=1.0}]

解法:如果参数是List类型,不要使用List.of形式创建list,否则会RPC调用报下面报错

相关推荐
小怪吴吴40 分钟前
idea 开发Android
android·java·intellij-idea
嘻嘻哈哈樱桃42 分钟前
牛客经典101题题解集--动态规划
java·数据结构·python·算法·职场和发展·动态规划
一次旅行44 分钟前
IDEA安装CC GUI新手指南
java·ide·intellij-idea
超梦dasgg1 小时前
Spring AI 智能航空助手项目实战
java·人工智能·后端·spring·ai编程
counting money2 小时前
Spring框架基础(配置篇)
java·后端·spring
秋92 小时前
OceanBase与GreatSQL在Java应用中的性能调优方法有哪些?
java·开发语言·oceanbase
今天又在写代码2 小时前
并发问题解决
java·开发语言·数据库
老王以为3 小时前
前端视角下的 Java
java·javascript·程序员
看腻了那片水3 小时前
开源一个对业务代码零侵入的透明数据治理框架 —— 【sangsang】
java·mybatis