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

如何排查SpringVaadinServlet类中的排序异常问题?

问题

在Vaadin应用中对UI条目排序时触发如下异常:

java.lang.IllegalArgumentException: Comparison method violates its general contract!
            at java.util.TimSort.mergeHi(TimSort.java:899) ~[?:1.8.0_275]
            at java.util.TimSort.mergeAt(TimSort.java:516) ~[?:1.8.0_275]
            at java.util.TimSort.mergeCollapse(TimSort.java:441) ~[?:1.8.0_275]
            at java.util.TimSort.sort(TimSort.java:245) ~[?:1.8.0_275]
            at java.util.Arrays.sort(Arrays.java:1512) ~[?:1.8.0_275]
            at java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:353) ~[?:1.8.0_275]
            at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:500) ~[?:1.8.0_275]
            at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:486) ~[?:1.8.0_275]
            at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472) ~[?:1.8.0_275]
            at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) ~[?:1.8.0_275]
            at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[?:1.8.0_275]
            at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:566) ~[?:1.8.0_275]
            at com.vaadin.data.provider.DataCommunicator.fetchItemsWithRange(DataCommunicator.java:379) ~[vaadin-server-8.5.2.jar:8.5.2]
            at com.vaadin.data.provider.DataCommunicator.sendDataToClient(DataCommunicator.java:341) ~[vaadin-server-8.5.2.jar:8.5.2]
            at com.vaadin.data.provider.DataCommunicator.beforeClientResponse(DataCommunicator.java:316) ~[vaadin-server-8.5.2.jar:8.5.2]
            at com.vaadin.server.communication.UidlWriter.write(UidlWriter.java:126) [vaadin-server-8.5.2.jar:8.5.2]
            at com.vaadin.server.communication.UidlRequestHandler.writeUidl(UidlRequestHandler.java:124) [vaadin-server-8.5.2.jar:8.5.2]
            at com.vaadin.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:92) [vaadin-server-8.5.2.jar:8.5.2]
            at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40) [vaadin-server-8.5.2.jar:8.5.2]
            at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1601) [vaadin-server-8.5.2.jar:8.5.2]
            at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:445) [vaadin-server-8.5.2.jar:8.5.2]
            at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) [tomcat-embed-core-9.0.14.jar:9.0.14]
            at $javax.servlet.http.HttpServlet$$FastClassBySpringCGLIB$$5ddc88f8.invoke(<generated>) [tomcat-embed-core-9.0.14.jar:?]
            at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) [spring-core-5.1.4.RELEASE.jar:5.1.4.RELEASE]
            at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749) [spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
            at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) [spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
            at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) [spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
            at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) [spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
            at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) [spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
            at com.abc.myapp.ui.MyServlet$$EnhancerBySpringCGLIB$$beeb36e1.service(<generated>) [classes/:?]

已在自定义MyServlet类的所有方法中添加调试点,但未捕获到错误入口,该类定义如下:

@Component("vaadinServlet")
@WebServlet(name = "MyServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = DiscoveryUI.class, productionMode = false)
public class MyServlet extends SpringVaadinServlet {
  ...
}

推测问题可能出在其父类SpringVaadinServlet中,需排查该异常是由数据错误还是其他原因导致。

排查方案

1. 定位违规的Comparator实现

异常核心是Comparison method violates its general contract!,说明排序用的Comparator违反了三大约定(自反性、对称性、传递性)。从堆栈看,排序发生在DataCommunicator.fetchItemsWithRange,和Servlet本身无关,重点排查:

  • 所有自定义Comparator:包括Grid等UI组件绑定的排序器、DataProvider中定义的排序逻辑;
  • 检查Comparator中的常见问题:
    • null值处理逻辑不一致(如a为null返回-1,b为null返回1,但两者都为null时返回非0)
    • 比较逻辑依赖外部可变状态(如动态变化的配置值)
    • 比较结果不符合传递性(如a > b,b > c,但a <= c)

2. 临时切换排序算法验证

Java 7+默认使用TimSort,对Comparator规范要求严格。添加JVM参数-Djava.util.Arrays.useLegacyMergeSort=true切换回旧的MergeSort:

  • 若异常消失,确认是Comparator的规范问题;
  • 若异常仍出现,再排查数据本身的问题。

3. 校验排序数据合法性

  • 提取触发排序时的数据集,用相同Comparator单独排序测试,复现异常后逐步排查异常数据(如null、非法枚举值、字段值异常的对象);
  • 检查数据对象的排序字段是否存在逻辑错误(如类型不匹配、计算字段的bug)。

4. 调试Vaadin数据处理流程

无需在Servlet中加断点,直接在以下核心方法加断点:

  • DataCommunicator.fetchItemsWithRange:查看传入的排序参数和数据集;
  • 追踪到具体Comparator执行的地方,观察每次比较的输入值和返回结果,找出违反约定的场景。

5. 排除Spring代理影响

临时禁用Spring对该Servlet的AOP代理,排除代理拦截逻辑对排序流程的干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 19:57:40