[spring-projects/spring-boot]ElasticsearchClientAutoConfiguration 导致全局 ObjectMapper 被覆盖

2024-06-26 755 views
8

使用 springboot 3.0.0 测试

当新的ElasticsearchClient在类路径中时,它会触发,ElasticsearchClientAutoConfiguration然后覆盖应用程序对象映射器/通过配置的映射器Jackson2ObjectMapperBuilder

此问题的重现者;https://github.com/manofthepeace/spring3-elasticClient-mapperissue

重现步骤;

  1. 通过测试运行测试mvn test将通过
  2. 修改 pom.xml 并取消注释 elasticsearch-java 依赖项
  3. mvn test通过测试运行测试将失败
  4. 正在TestingWebApplication.java使用@SpringBootApplication(exclude = ElasticsearchClientAutoConfiguration.class)
  5. mvn test通过测试运行测试将通过

预期行为;应该RestClientTransport使用新的 ObjectMapper,或者用户提供的 ObjectMapper,而不是 spring-mvc / 全局使用的 ObjectMapper。可以使用new JacksonJsonpMapper()或通过将 objectMapper 传递给JacksonJsonpMapper构造函数来完成。

回答

6

elasticsearch-java您可以在中指定版本pom.xml

        <dependency>
            <groupId>co.elastic.clients</groupId>
            <artifactId>elasticsearch-java</artifactId>
            <version>7.17.7</version>
        </dependency>

当删除该版本并让 Spring Boot 处理该版本时,问题就消失了。

0

您的版本似乎接受了传入的内容ObjectMapperorg.springframework.boot.autoconfigure.elasticsearch.ElasticsearchClientConfigurations.JacksonJsonpMapperConfiguration#jacksonJsonpMapper对其进行了重新配置。 boot 使用的版本(撰写本文时为 8.5.1)不再执行此操作。

4

@mhalbritter 感谢您抽出时间并回答这个问题。

我发现当用户指定自己的库(而不是启动器)时,spring 会尝试进行一些自动配置,这真的很不幸。elasticsearch 客户端根本不向后兼容,因此使用 springboot 提供的客户端对我们来说不是一个选择,因为我们需要支持大量的 elastic 服务器版本(不是那么大,但有 5.X、6.X 和 7.X),我们不能仅仅因为 elastic 服务器(我们无法控制)而落后于 spring 版本。

如果存在启动器类之一(而不是来自 elasticsearch 的类),则自动配置会运行,我将不胜感激。我必须说,尝试了解为什么我们的 rest api 停止提供正确的响应是一次相当痛苦的调试,现在我们所有使用 ES 的微服务都需要那个特定的排除项,我觉得这有点肮脏。

我理解添加启动器会修改我的应用程序中的行为,但是提供 spring 之外的库,我认为它们应该保持不变,对我来说这似乎确实是一个问题。

我还认为让 elasticsearch 库对全局对象映射器进行任何操作都是有风险的,也许它在 8.5.1 中确实可以对全局对象映射器进行任何操作,但谁知道将来它们不会这样做。我仍然认为它需要自己的,用户可能希望以不同于通过 rest 的方式解析 Es 响应。

再次感谢您抽出时间。

5

如果您确实想要,您仍然可以使用旧客户端。自动配置的构建方式是,如果您提供自己的JsonpMapper,它将退出,可以这样做:

@Configuration(proxyBeanMethods = false)
class JacksonJsonpMapperConfiguration {
    @Bean
    JacksonJsonpMapper jacksonJsonpMapper() {
        return new JacksonJsonpMapper();
    }
}

这对你有用吗?

是的,我同意 ElasticSearch 修改传递的内容ObjectMapper并不好。我会标记它以供讨论,看看团队其他成员怎么想。

9

所以今天我们讨论了这个问题,并决定我们所有的选择都是不好的:

我们要么改变不传递全局变量的默认值ObjectMapper,这会破坏所有依赖当前行为的人。

或者我们让它保持原样,并接受全局对象映射器用于 WebMVC 和 ElasticSearch 以及其他任何内容,如果您重新配置它,则您可以在所有地方重新配置它。

elasticsearch 客户端重新配置给定的ObjectMapper一个错误,他们已经修复了该错误。

我将创建一张票,也许可以翻转 Spring Boot 3.1 中的默认设置:不使用ObjectMapperElasticSearch 的全局变量,而是使用 的无参数构造函数JacksonJsonpMapper

2

我同意以上所有观点。我也认为这是/曾经是 elasticsearch 客户端的一个错误,遗憾的是它没有被移植。

我尝试了您的建议,虽然它确实有效,但它仍然会继续创建 foobar ElasticsearchClientbean。我说的是 foobar,因为项目/属性中没有 RestClient 配置,所以客户端 bean 永远不会工作,我认为这是另一个有点奇怪的行为,因为它占用了一些资源。

我认为在我的情况下最好的方法是通过禁用 bean AutoConfigurationImportSelector,这样所有的微服务都可以有这个排除并让我手动处理它,这样我就没有多余的了objectmapper,这也是它的公平份额资源+bean ElasticsearchClient

在我的应用程序中,我有一个围绕该客户端的包装器 bean,从该包装器 bean 中,我现在可以选择高级客户端和新客户端,这样我们就可以平稳缓慢地迁移,而且由于它在某些地方用于非 spring 组件,所以不必到处传递两者,而只需传递包装器即可。这就是为什么自动配置会继续创建 bean 的原因。(当底层 restclient 根本没有配置时,仍然认为创建 bean 很奇怪)

谢谢您的帮助和时间。

4

如果我理解正确的话,您希望自己使用 elasticsearch 客户端,而不是使用 spring (boot) 提供的内容。在这种情况下,正确的解决方案是排除自动配置,这样它根本就不会运行。这样,您的上下文中就不会有任何不需要的 bean。

7

确实如此。拥有多个具有不同 es 客户端版本的分支来支持多个 es 服务器版本更容易维护,但所有这些分支都受益于拥有最新和最好的启动版本及其带来的好处。

2

一种解决方法是有条件地创建副本ObjectMapper并对其进行修改以使其与 Elastic 兼容:

@Bean
JacksonJsonpMapper jacksonJsonpMapper(ObjectMapper objectMapper) {
    return new JacksonJsonpMapper(makeElasticCompatible(objectMapper));
}

private ObjectMapper makeElasticCompatible(ObjectMapper objectMapper) {
    if (!objectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
        return objectMapper;
    }
    return objectMapper.copy().disable(SerializationFeature.INDENT_OUTPUT);
}

我想知道我们是否应该在 Boot 中执行此操作,或者也许向 Elastic 团队建议?

3

我们认为应该在自动配置中实现这一点co.elastic.clients.json.jackson.JacksonJsonpMapper。如果用户想要自定义映射器,他们可以覆盖bean(可能使用任何co.elastic.clients.json.JsonpMapper实现)。

3

版本 3.0.3 破坏了我的工作代码? 5ADF53A3-E7A0-454C-9870-FC2E06171327

这个 bean 没有修复问题:

@Configuration(proxyBeanMethods = false)
class ElasticJsonConfig{
    @Bean
    fun jacksonJsonpMapper() = JacksonJsonpMapper(
        jacksonObjectMapper().findAndRegisterModules()
            .disable(FAIL_ON_UNKNOWN_PROPERTIES)
    )
}

回滚到 spring boot 3.0.2 可以完美运行。


我通过更新 elastic-search 配置文件修复了这个问题.../src/main/resources/settings/settings.json。我删除了此行(它是未知属性):

{
        "autocomplete_search": {"tokenizer": "lowercase"}
}
7

@steklopod 很遗憾听到这个消息。应该可以使用这样的 bean 恢复以前的行为:

@Bean
JacksonJsonpMapper jacksonJsonpMapper(ObjectMapper objectMapper) {
    return new JacksonJsonpMapper(objectMapper);
}

Elastic 随后将再次使用自动配置ObjectMapper。请注意,如果您使用输出缩进,这将导致问题。

如果上述方法不起作用,请使用重现该问题的最小样本来打开一个新问题。

1

感谢@wilkinsona 的反馈。

老实说,这对我来说不是什么大问题。只是想和你分享信息。有趣的是,它只在 github-actions ci 中失败。在本地 mac 上不会。

似乎在版本中3.0.3有一些针对弹性配置的新验证规则settings.json。我的意思是这个文件:@Setting(settingPath = "/settings/settings.json")。如果 linux 环境中有一个未知属性 - 它会导致应用程序失败。

0

在 Spring Boot 中,我们不会对该文件执行任何操作。不过,Elastic 客户端代码中的此区域很可能发生了一些变化。