Java Spring中RestHighLevelClient存索引抛空指针但数据已写入ES
问题现象
使用RestHighLevelClient向Elasticsearch写入数据时,数据已成功写入(可通过/index/_search查询确认),但程序抛出以下异常:
2024-01-21 23:19:28.724 ERROR 14382 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataAccessResourceFailureException: Unable to parse response body for Response{requestLine=PUT /product/_doc/98?timeout=1m HTTP/1.1, host=http://localhost:9200, response=HTTP/1.1 201 Created}; nested exception is java.lang.RuntimeException: Unable to parse response body for Response{requestLine=PUT /product/_doc/98?timeout=1m HTTP/1.1, host=http://localhost:9200, response=HTTP/1.1 201 Created}] with root cause
java.lang.NullPointerException: null
at java.base/java.util.Objects.requireNonNull(Objects.java:221) ~[na:na]
at org.elasticsearch.action.DocWriteResponse.(DocWriteResponse.java:116) ~[elasticsearch-7.15.2.jar:7.15.2]
at org.elasticsearch.action.index.IndexResponse.(IndexResponse.java:43) ~[elasticsearch-7.15.2.jar:7.15.2]
对应的RestHighLevelClient配置代码:
package com.springPractice.app.configuration; import org.elasticsearch.client.RestHighLevelClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.data.elasticsearch.client.ClientConfiguration; import org.springframework.data.elasticsearch.client.RestClients; import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration; import org.springframework.http.HttpHeaders; @Configuration @ComponentScan(basePackages = {"com.springPractice.app"}) @EnableAutoConfiguration public class ElasticsearchConfiguration extends AbstractElasticsearchConfiguration { private final ElasticsearchProperties elasticsearchProperties; @Autowired public ElasticsearchConfiguration(ElasticsearchProperties elasticsearchProperties) { this.elasticsearchProperties = elasticsearchProperties; } @Override @Bean public RestHighLevelClient elasticsearchClient() { final ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo("localhost:9200") .withSocketTimeout(elasticsearchProperties.getSocketTimeout()) .build(); return RestClients.create(clientConfiguration).rest(); } }
问题原因
- 版本不兼容:Spring Data Elasticsearch、RestHighLevelClient与Elasticsearch服务器版本不匹配,导致响应体解析时字段缺失,触发空指针。
- 配置冲突:自定义配置类同时使用
@EnableAutoConfiguration和继承AbstractElasticsearchConfiguration,导致Spring自动配置与自定义配置产生冲突,客户端初始化异常。 - 依赖冲突:项目中存在多个版本的Elasticsearch相关依赖,导致类加载异常。
解决方案
1. 确保版本完全匹配
严格遵循Spring Data Elasticsearch与Elasticsearch的官方版本对应关系,例如:
- Spring Data Elasticsearch 4.3.x 对应 Elasticsearch 7.15.x
- 确保
spring-boot-starter-data-elasticsearch、elasticsearch-rest-high-level-client的版本与ES服务器版本完全一致。
2. 简化客户端配置
去掉自定义配置类中的@EnableAutoConfiguration,避免自动配置冲突;或者直接使用Spring Boot自动配置,无需自定义配置类:
在application.properties中添加配置:
spring.elasticsearch.uris=http://localhost:9200 spring.elasticsearch.socket-timeout=30s
随后删除自定义的ElasticsearchConfiguration类,Spring会自动创建符合要求的RestHighLevelClient实例。
3. 清理依赖冲突
检查项目依赖文件(Maven的pom.xml或Gradle的build.gradle),确保只引入一套Elasticsearch相关依赖,排除重复或版本不一致的依赖。例如Maven中用<dependencyManagement>统一管理版本:
<dependencyManagement> <dependencies> <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>7.15.2</version> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>7.15.2</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-elasticsearch</artifactId> <version>4.3.5</version> </dependency> </dependencies> </dependencyManagement>
4. 修正自定义配置(可选)
如果必须保留自定义配置,移除@EnableAutoConfiguration并调整配置逻辑:
@Configuration @ComponentScan(basePackages = {"com.springPractice.app"}) public class ElasticsearchConfiguration extends AbstractElasticsearchConfiguration { @Value("${spring.elasticsearch.uris}") private String esUris; @Value("${spring.elasticsearch.socket-timeout}") private Duration socketTimeout; @Override public RestHighLevelClient elasticsearchClient() { final ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo(esUris.split(",")) .withSocketTimeout(socketTimeout) .build(); return RestClients.create(clientConfiguration).rest(); } }
内容的提问来源于stack exchange,提问作者Aravind

