CSEJavaSDK 与 Spring1.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提出过两个解决方案,分别是:
将 hibernate-validator 给 exclude 掉
在项目中重新引入 validation-api,显式声明版本号为 2.0.1.Final
对这两种解决方案的 dependency tree 进行分析,发现:
将 hibernate-validator 给 exclude 掉之后,项目中不包含 hibernate-validator,仍然存在 validation-api,版本号为 1.1.0.Final
重新引入 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 的结构也与之前保持一致。
欢迎大家对这个问题积极进行讨论。
- 点赞
- 收藏
- 关注作者
评论(0)