Spring Boot 2 war file attempts to load el-api v3.0 class (NoClassDefFoundError: javax/el/ELManager) on tomcat 7 (el-api 2.2)

Spring Boot 2 war file attempts to load el-api v3.0 class (NoClassDefFoundError: javax/el/ELManager) on tomcat 7 (el-api 2.2)

我有一个 Spring boot 2 gradle 项目,我希望将其作为(不可执行的)war 文件部署到 tomcat 7 实例(RHEL ).

我在服务器上将 war 部署到 tomcat 时收到 NoClassDefFoundError

...
Caused by: org.hibernate.cfg.beanvalidation.IntegrationException: Error activating Bean Validation integration
        at org.hibernate.cfg.beanvalidation.BeanValidationIntegrator.integrate(BeanValidationIntegrator.java:138)
        at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:281)
        at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:462)
        at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:892)
        at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57)
        at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390)
        ... 34 more
Caused by: java.lang.NoClassDefFoundError: javax/el/ELManager
        at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:88)
        at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.<init>(ResourceBundleMessageInterpolator.java:47)
        at org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultMessageInterpolator(ConfigurationImpl.java:474)
        at org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultMessageInterpolatorConfiguredWithClassLoader(ConfigurationImpl.java:650)
        at org.hibernate.validator.internal.engine.ConfigurationImpl.getMessageInterpolator(ConfigurationImpl.java:397)
        at org.hibernate.validator.internal.engine.ValidatorFactoryImpl.<init>(ValidatorFactoryImpl.java:183)
        at org.hibernate.validator.HibernateValidator.buildValidatorFactory(HibernateValidator.java:38)
        at org.hibernate.validator.internal.engine.ConfigurationImpl.buildValidatorFactory(ConfigurationImpl.java:364)
        at javax.validation.Validation.buildDefaultValidatorFactory(Validation.java:103)
        at org.hibernate.cfg.beanvalidation.TypeSafeActivator.getValidatorFactory(TypeSafeActivator.java:501)
        at org.hibernate.cfg.beanvalidation.TypeSafeActivator.activate(TypeSafeActivator.java:84)
        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.hibernate.cfg.beanvalidation.BeanValidationIntegrator.integrate(BeanValidationIntegrator.java:132)
        ... 40 more

经过检查,我发现 hibernate bean 验证库在初始化期间未能找到这个特定的 class (javax.el.ElManager)。

解决方案 建议将正确的 el-api v3.0 依赖添加到 运行time 但我不确定这是一个明智的解决方案,因为 el- api v2.2 已经存在于 Tomcat 中的共享(gradle 白话中的 providedRuntime)库中。如果我有两个相同的版本 API,我会担心 ClassLoader 问题(这是一个合理的问题吗?)。

Upgrading to tomcat 8 is a no-go, as we run tomcat7 on RHEL which currently only supports tomcat 7.

如何告诉 Spring 使用 javax el-api v2.2?

补充说明

依赖spring-boot-starter-tomcat时的默认tomcat.version根据gradlew dependencies命令似乎是8.5.31:

+--- org.springframework.boot:spring-boot-starter-tomcat:2.0.2.RELEASE
|    +--- javax.annotation:javax.annotation-api:1.3.2
|    +--- org.apache.tomcat.embed:tomcat-embed-core:8.5.31
|    +--- org.apache.tomcat.embed:tomcat-embed-el:8.5.31
|    \--- org.apache.tomcat.embed:tomcat-embed-websocket:8.5.31

据我了解 ,将 gradle ext['tomcat.version'] 属性 设置为 7.0.76(我们服务器的版本)可以正确解析这些依赖关系:

+--- org.springframework.boot:spring-boot-starter-tomcat:2.0.2.RELEASE
|    +--- javax.annotation:javax.annotation-api:1.3.2
|    +--- org.apache.tomcat.embed:tomcat-embed-core:8.5.31 -> 7.0.76
|    +--- org.apache.tomcat.embed:tomcat-embed-el:8.5.31 -> 7.0.76
|    \--- org.apache.tomcat.embed:tomcat-embed-websocket:8.5.31 -> 7.0.76
|         \--- org.apache.tomcat.embed:tomcat-embed-core:7.0.76

不过我还是运行进入这个NoClassDefFoundError。在我看来 Spring 无法识别正确的 api 版本并继续假设 ElManager 存在。

Thank you for your time.

Spring开机2.X只支持Tomcat>=8.5.X

在尝试修改默认 spring 配置(即 hibernate-validator)的传递依赖性之后,我发现了 Spring Boot 2 的 minimum server requirements: Tomcat 8.5.X. Alongside that, only servlet APIs of 3.1+ are supported[=14] 的文档=]

我在这里要解决的根本问题 - 如果我更改依赖项,这无疑会产生问题 - Tomcat 7 不受 Spring 支持启动 2。我降级的任何传递库都可能会破坏其他一些需要其更新功能的依赖项。

Spring 启动 1.5.X does however support Tomcat 7 and Servlet 3.0 应用程序服务器。所以 解决方案是降级到 Spring Boot 1.5.X(在撰写本文时目前为 1.5.13)。

或者 - 我将在我的工作场所向系统团队提出的解决方案 - 改为使用嵌入式 servlet 容器(即 tomcat 8.5/9嵌入式)。对我来说不幸的是,这意味着对假设部署机制的现有企业流程的修改进行长时间的讨论。但这不是重点:P