前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一个离奇的ArrayIndexOutOfBoundsException异常的排查过程

一个离奇的ArrayIndexOutOfBoundsException异常的排查过程

作者头像
用户1516716
发布2018-12-24 16:43:57
9.3K1
发布2018-12-24 16:43:57
举报
文章被收录于专栏:A周立SpringCloudA周立SpringCloud

今天同事遇到了一个离奇的ArrayIndexOutOfBoundsException,找我协助定位,定位的过程很有意思,故而记录一下。

先按时序复盘一下

  • 项目原先可正常运行。
  • 没有修改任何依赖的情况下,从另一个项目移植了工具类BeanValidationUtil 后,报如下异常: org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:307) | Context initialization failed org.springframework.beans.factory.BeanDefinitionStoreException: Failed to read candidate component class: file [/Users/xxxxxxxxx/BeanValidationUtil.class]; nested exception is java.lang.ArrayIndexOutOfBoundsException: 48959 at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents(ClassPathScanningCandidateComponentProvider.java:260) at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:242) at org.springframework.context.annotation.ComponentScanBeanDefinitionParser.parse(ComponentScanBeanDefinitionParser.java:84) at org.springframework.beans.factory.xml.NamespaceHandlerSupport.parse(NamespaceHandlerSupport.java:73) at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1419) at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1409) at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:184) at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:140) at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:111) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:493) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:334) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:174) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:209) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:180) at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125) at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94) at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:131) at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:522) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:436) at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:384) at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:283) at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5135) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5658) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145) at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:1015) at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:991) at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:652) at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:2015) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301) at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819) at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801) at org.apache.catalina.mbeans.MBeanFactory.createContext(MBeanFactory.java:789) at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:573) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301) at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819) at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801) at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1468) at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:76) at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1309) at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1401) at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:829) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:324) at sun.rmi.transport.Transport$1.run(Transport.java:200) at sun.rmi.transport.Transport$1.run(Transport.java:197) at java.security.AccessController.doPrivileged(Native Method) at sun.rmi.transport.Transport.serviceCall(Transport.java:196) at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:568) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683) at java.security.AccessController.doPrivileged(Native Method) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: java.lang.ArrayIndexOutOfBoundsException: 48959 at org.springframework.asm.ClassReader.readUnsignedShort(Unknown Source) at org.springframework.asm.ClassReader.accept(Unknown Source) at org.springframework.asm.ClassReader.accept(Unknown Source) at org.springframework.core.type.classreading.SimpleMetadataReader.<init>(SimpleMetadataReader.java:54) at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:80) at org.springframework.core.type.classreading.CachingMetadataReaderFactory.getMetadataReader(CachingMetadataReaderFactory.java:101) at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents(ClassPathScanningCandidateComponentProvider.java:236) ... 68 more

造成异常的工具类

代码语言:javascript
复制
/**
 * JSR349 Bean Validation工具类
 *
 * @author limu.zl
 */
public class BeanValidationUtil {
    /**
     * 验证失败时抛出ConstraintViolationException
     *
     * @param object 待验证对象
     * @param groups 验证group
     */
    public static void validateWithException(Object object, Class<?>... groups) {
        ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();

        // 国际化,强制用中文,ref:https://docs.oracle.com/javase/tutorial/i18n/locale/create.html
        Locale locale = new Locale.Builder().setLanguage("zh").setRegion("CN").build();

        // 国际化,指定语言,ref:https://www.ibm.com/developerworks/cn/java/j-cn-hibernate-validator/index.html
        MessageInterpolator interpolator = new LocalizedMessageInterpolator(validatorFactory.getMessageInterpolator(), locale);

        Validator classValidator = validatorFactory.usingContext()
                .messageInterpolator(interpolator)
                .getValidator();

        Set<ConstraintViolation<Object>> constraintViolations = classValidator.validate(object, groups);
        if (!constraintViolations.isEmpty()) {
            String messages = constraintViolations.stream()
                    .filter(t -> t instanceof ConstraintViolationImpl)
                    .map(t -> {
                        ConstraintViolationImpl t1 = (ConstraintViolationImpl) t;
                        return String.format("出问题的字段:%s,问题:%s", t1.getPropertyPath(), t1.getMessage());
                    })
                    .collect(Collectors.toList())
                    .toString();
            throw new IllegalArgumentException(messages);
        }
    }
}

分析

如代码所示,BeanValidationUtil 是个工具类,根本不在Spring容器上下文里,但异常栈却报到Spring的包里去了。

  • 由于项目没有修改依赖,所以包冲突问题不太可能出现(这个项目原先也使用JSR349做Bean Validation),而且冲突的话异常应该是NoClassDefFoundError之类的异常。
  • 尝试降低Hibernate Validation的版本到4.x,故障依旧。
  • 百度、谷歌类似异常,无果。

经过20分钟的源码定位也没找到问题所在,于是我尝试逐步删除BeanValidationUtil的代码。发现当把lambda语法删光之后,项目就能正常启动了。

突然灵光一现,问同事 : “这TM是不是个非常古老的项目啊?”

同事:“对啊,四五年了吧……”

于是分析了下pom.xml,发现用的是Spring 3 。隐约记得Spring 3不完全兼容JDK8,这个类中使用了Java 8的语法,所以导致了问题。

结果确认

既然猜测是Spring 3和Java 8不兼容导致,故而在搜索时,将关键词改为:spring 3 java 8 ArrayIndexOutOfBoundsException ,果然印证了自己的想法。

  • ArrayOutOfBoundsException on Bean creation while using Java 8 constructs
  • Spring BeanDefinitionStoreExcept-nested exception is java.lang.ArrayIndexOutOfBoundsException: 53804

反思

犯了经验主义错误,基于Spring 3的项目已经三四年没有见过了(Dubbo不算,哈哈哈。因为Dubbo当初的版本虽然依赖了Spring 3,但其实实际项目一般都会exclude掉,换上Spring 4),一直以为是个Spring 4的项目,没有从Spring版本与JDK的兼容性的方向上去考虑。

浪费了半小时。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-12-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 A周立SpringCloud 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 先按时序复盘一下
  • 造成异常的工具类
  • 分析
  • 结果确认
  • 反思
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档