CSEJavaSDK 与 Spring1.5 部分版本依赖冲突导致 NoClassDefFoundError 分析及解决方案

举报
Leonang 发表于 2019/08/01 15:53:52 2019/08/01
【摘要】 在使用 CSEJavaSDK 2.3.47 及以后的版本(目前到2.3.69)的时候,如果项目声明了 parent 项目为 springboot 1.5,则会因为版本号覆盖导致项目启动时报 NoClassDefFoundError 错误。本文提供了问题原因的分析和问题的解决方案。

问题描述

在使用 CSEJavaSDK 2.3.47 及以后的版本(目前到2.3.69)的时候,如果项目声明了 parent 项目为 springboot 1.5,则会因为版本号覆盖导致项目启动时报 NoClassDefFoundError 错误。之前已经有人在华为云社区提到过这个问题:https://bbs.huaweicloud.com/forum/thread-18873-1-1.html


具体报错信息如下:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'standardJacksonObjectMapperBuilderCustomizer' defined in class path resource [org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration.class]: Unsatisfied dependency expressed through method 'standardJacksonObjectMapperBuilderCustomizer' parameter 1; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'spring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties': Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: org/hibernate/validator/internal/engine/DefaultClockProvider
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:467)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1178)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1072)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:511)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)
at com.service.bugdemo.BugdemoApplication.main(BugdemoApplication.java:11)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'spring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties': Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: org/hibernate/validator/internal/engine/DefaultClockProvider
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1136)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1064)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:835)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
... 18 more
Caused by: java.lang.NoClassDefFoundError: org/hibernate/validator/internal/engine/DefaultClockProvider
at org.hibernate.validator.internal.engine.ConfigurationImpl.<init>(ConfigurationImpl.java:135)
at org.hibernate.validator.internal.engine.ConfigurationImpl.<init>(ConfigurationImpl.java:108)
at org.hibernate.validator.HibernateValidator.createGenericConfiguration(HibernateValidator.java:33)
at javax.validation.Validation$GenericBootstrapImpl.configure(Validation.java:276)
at org.springframework.boot.validation.MessageInterpolatorFactory.getObject(MessageInterpolatorFactory.java:53)
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor$ValidatedLocalValidatorFactoryBean.<init>(ConfigurationPropertiesBindingPostProcessor.java:412)
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.getValidator(ConfigurationPropertiesBindingPostProcessor.java:369)
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.determineValidator(ConfigurationPropertiesBindingPostProcessor.java:353)
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:315)
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:292)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:407)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1623)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)


问题定位

查看了出问题项目的 denpendency tree,发现其中有以下记录:

[INFO] |     +- org.hibernate.validator:hibernate-validator:jar:6.0.14.Final:compile

[INFO] |     +- javax.validation:validation-api:jar:1.1.0.Final:compile

由此可见此项目中, javax.validation:validation-api 的版本为 1.1.0.Final


wuliuqi666 先后在 https://bbs.huaweicloud.com/forum/thread-18873-1-1.html 和 https://bbs.huaweicloud.com/blogs/2700e5d390fe11e9b759fa163e330718提出过两个解决方案,分别是:

  1. 将 hibernate-validator 给 exclude 掉

  2. 在项目中重新引入 validation-api,显式声明版本号为 2.0.1.Final

对这两种解决方案的 dependency tree 进行分析,发现:

  1. 将 hibernate-validator 给 exclude 掉之后,项目中不包含 hibernate-validator,仍然存在 validation-api,版本号为 1.1.0.Final

  2. 重新引入 validation-api 之后,项目中同时包含 hibernate-validator 和 validation-api,validation-api 的版本号为 2.0.1.Final,但是 hibernate-validation 和 validation-api 的 dependency 所属层级发生了变化。

可以得出结论:该问题是由于 hibernate-validator 与 validation-api 的版本冲突导致,hibernate 6.0.14 可以和 validaton-api 2.0.1 共存,在和 validation-api 1.1.0 共存时就会报 NoClassDefFoundError 错误。


开始怀疑是 ServiceComb 导致的,因为使用旧版 ServiceComb 并不存在这个问题。但是在对 CSE 依赖的开源项目 ServiceComb 进行分析之后,发现 ServiceComb 中的 javax.validation:validation-api 的版本为 2.0.1.Final,所以该问题不是 ServiceComb 引起的。查看项目的 pom 文件,发现项目引入了 com.huawei.paas.cse:cse-dependency(2.3.69) 作为 DM,同时引入 org.springframework.boot:spring-boot-stater-parent(1.5.9) 作为 parent 项目。由于 parent 中的版本声明优先级高于 DM,所以进行排查,发现在将 parent 依赖取消掉之后,项目的 validation-api 版本由 1.1.0.Final 变成了 2.0.1.Final,由此可以定位出版本覆盖是由 springboot 导致的。


进一步对 spring-boot-starter-parent 进行分析,发现 spring-boot-starter-parent 又以 spring-boot-dependencies 作为父项目,而在 spring-boot-dependencies 中,又有以下声明:

<javax-validation.version>1.1.0.Final</javax-validation.version>

由于 parent 项目比 DM 的 dependency 版本声明优先级更高,所以导致了项目的 validation-api 版本问题。


问题解决

由于此问题是 parent 项目的版本覆盖造成的,所以只能对项目的 pom 文件修改进行解决。


此前提到的两个解决方案,或多或少对 dependency 的结构造成影响,均存在一定的潜在风险。本着修改对项目影响最低和对使用依赖的框架(ServiceComb)影响最低的原则,此处推荐另一种解决方案,在项目的 pom 文件的 properties 标签下添加一条:

<javax-validation.version>2.0.1.Final</javax-validation.version>

这样可以覆盖掉 spring-boot-dependency 中的版本号声明,将此项目的 validation-api 版本修改为 2.0.1.Final。再对项目的 dependency tree 与报错时作对比,发现除了 validation-api 的版本号更改了以外,其它没有任何地方被修改,dependency 的结构也与之前保持一致。


欢迎大家对这个问题积极进行讨论。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。