【问题标题】:Possible to ignore configuration classes on the classpath?可以忽略类路径上的配置类吗?
【发布时间】:2021-11-22 16:59:34
【问题描述】:

我有一个 Spring Boot 应用程序在使用嵌入式 tomcat 运行时按预期工作,但我注意到如果我尝试从我在以前项目中使用的现有 tomcat 实例运行它,那么它会失败并出现 NoClassDefFoundError我在应用程序的任何地方都没有使用的类。

我注意到在 /lib 目录中我有一个包含几个 Spring 注释类的 jar,因此作为测试,我清理了 /lib 目录来解决问题。我的假设是 Spring 在类路径中看到了一些配置/beans/imports,因为它们存在于 /lib 目录中,并且要么尝试自行自动配置某些东西,要么实际上试图实例化其中的一些类。

那么我的问题是 - 假设我不能总是完全控制类路径上所有内容的内容,我该如何防止发生这样的错误?

编辑

更详细一点 - 未找到的类是 DefaultCookieSerializer,它是 spring-session-implementation 依赖项的一部分。它被拉入位于 /lib 的 jar 中的一个类中,但它不是我的应用程序的任何部分。

【问题讨论】:

  • 完整的堆栈跟踪可能很有用...
  • @30thh - 当已知原因时,我看不出堆栈跟踪有什么帮助。 Spring 正在尝试基于在 tomcat /lib 目录中找到的类进行自动配置。这些类不是导致NoClassDefFoundError 的项目的依赖项。据我所知,我无法阻止 spring 从我的项目中拉入 tomcat /lib,我必须修改 tomcat 配置本身,导致这篇文章。
  • 问题不是“是什么导致了这个问题”,而是“有什么好的解决方案可以防止这个问题的发生”。回顾我的帖子,这应该很明显,在这里为您粘贴相关的内容,希望您这次能阅读。 So then my question is - assuming I can't always fully control the contents of everything on the classpath, how can I prevent errors like this from occurring?
  • 对于 Spring 在服务器的类路径中拾取类没有通用的解决方案(参见例如 this question)。对于您的具体问题,可能有多种解决方案,但需要堆栈跟踪才能了解 Spring 为何尝试加载该类。

标签: java spring spring-boot spring-mvc tomcat


【解决方案1】:

您可以从主类中删除@SpringBootApplication 注释,并将其替换为@ComponentScan 注释和明确列出您要加载的配置类的@Import 注释。例如,在一个使用指标、Web 客户端、REST 模板、Jackson 等的 Spring Boot MVC 应用程序中,我能够用下面的代码替换 @SpringBootApplication 注释,并通过所有功能测试让它像以前一样工作通过:

@Import({ MetricsAutoConfiguration.class,
        InfluxMetricsExportAutoConfiguration.class,
        ServletWebServerFactoryAutoConfiguration.class,
        DispatcherServletAutoConfiguration.class,
        WebMvcAutoConfiguration.class,
        JacksonAutoConfiguration.class,
        WebClientAutoConfiguration.class,
        RestTemplateAutoConfiguration.class,
        RefreshAutoConfiguration.class,
        ValidationAutoConfiguration.class
})
@ComponentScan

【讨论】:

    【解决方案2】:

    上述异常的可能罪魁祸首是类路径上不兼容的 jar。

    由于我们不知道您遇到的问题是哪个库,我们无法告诉您确切的原因,但情况看起来是这样的:

    1. 其中一个 Spring-Boot 自动配置类被类路径中的类触发
    2. Trigerred 配置会尝试创建一些您拥有的 jar 中不存在的类 bean(但它在 Spring BOM 中提到的特定版本中)

    版本不兼容也可能导致MethodNotFound 异常。

    这就是为什么最好不要在容器内运行 Spring Boot 应用程序(使 jar 不是战争)而是作为具有嵌入式容器的可运行 jar 的原因之一。

    甚至在 Spring Boot 之前,最好考虑运行时类路径中存在的库并将它们标记为在项目中提供。在类路径上拥有不同版本的库可能会导致奇怪的 ClassCastExceptions 两端名称匹配,但其余名称不匹配。

    您可以通过禁用导致问题的自动配置来解决特定情况。您可以通过将exclude 添加到您的@SpringBootApplication 或使用属性文件来做到这一点。

    编辑: 如果您在 Spring Boot 应用程序中不使用非常广泛的包扫描(或在包扫描中使用项目外部的包名称),那么 Spring Boot 不太可能只是从类路径中导入配置。 正如我之前提到的,它是由类路径中存在的类触发的一些自动配置。

    理论解决方案: 您可以使用 maven shade 插件将所有包重新定位到您自己的包空间中:see docs。 问题是你会面临:

    1. 定义非常广泛的重定位模式,排除需要使用的 JEE 类,以便容器知道如何运行您的应用程序。
    2. 重定位很可能不会影响在 Spring Boot 注释中用作字符串的包名称(如注释 @PackageScan 或 @ConditionalOnClass)。据我所知,它尚未实施。您必须自己实现它 - 可能作为某种阴影插件资源处理器。
    3. 重定位类时,您必须替换 jar 中所有相关配置中的包名称。可能还会合并其中的一些。
    4. 您还必须考虑您使用的库或 spring 使用包名称或文件的方式。

    这绝对不是一项微不足道的任务,前方有许多陷阱。但是如果做得对,那么它可能会让你忽略容器类路径上的内容。 Spring Boot 还会在重定位的包中查找类,而您不会在普通的 jar 中找到这些类。

    【讨论】:

      【解决方案3】:

      检查@EnableAutoConfiguration 提供的功能。您可以为您的应用程序显式配置一组自动配置类。 This tutorial 是一个很好的起点。

      【讨论】:

      • 似乎对缺少由 tomcats /lib 类加载器自动加载的类的依赖项没有任何影响,即使这样做了,它也不允许将来对随机类路径库进行校对。
      猜你喜欢
      • 2011-08-17
      • 2023-03-04
      • 2016-11-04
      • 2021-09-13
      • 2011-10-12
      • 2015-03-07
      • 2018-04-01
      • 1970-01-01
      • 2015-02-01
      相关资源
      最近更新 更多