SpringBoot项目实战:从零到一集成Elasticsearch 8.x并构建通用数据访问层

1. 为什么我们需要一个通用的ES数据访问层?

如果你正在开发一个需要搜索功能的SpringBoot应用,比如一个电商网站的商品搜索,或者一个日志分析平台,那么Elasticsearch(ES)大概率是你的首选。它强大的全文检索和聚合分析能力,确实能解决很多问题。但说实话,我刚开始在项目里用ES的时候,感觉挺“酸爽”的。每次写查询,都要面对一大堆DSL(领域特定语言)构建代码,BoolQueryBuilderTermQueryBuilder这些类名又长又绕,业务代码里到处散落着ES的API调用,维护起来简直是噩梦。更别提版本升级了,从7.x换到8.x,官方直接弃用了RestHighLevelClient,改用全新的Java API Client,很多写法都变了,改起来那叫一个头疼。

所以,我就在想,能不能像我们用Spring Data JPA操作数据库那样,来操作Elasticsearch呢?我们定义一个实体类,然后写一个接口,基础的增删改查、分页查询就自动有了。这就是构建一个通用数据访问层(Repository) 的核心想法。它的好处太明显了:第一是省事,业务开发不用再关心ES复杂的DSL语法,专注业务逻辑就行;第二是统一,所有ES操作都通过这一层,代码风格一致,也便于做统一的日志、监控和异常处理;第三是好维护,ES客户端配置、版本升级的变动,都只需要在这一层调整,不会波及到上层业务代码。

这篇文章,我就带你从零开始,在一个SpringBoot 3.x项目里,集成最新的Elasticsearch 8.x Java客户端,并一步步设计、实现一个高度抽象、拿来即用的通用Repository。我会用一个“商品搜索”的场景贯穿始终,让你看到每一步代码是如何解决实际问题的。最终,你会得到一个封装良好的工具包,以后在任何SpringBoot项目里需要ES,直接把这个包引入,简单配置一下就能用,开发效率能提升好几倍。

2. 环境准备与项目初始化

万事开头难,但第一步往往是最简单的。我们先来把基础环境搭好。

2.1 本地运行Elasticsearch和Kibana

虽然生产环境通常是集群部署,但我们本地开发,用Docker跑个单节点是最方便的。如果你还没装Docker,去官网下一个,安装过程很简单。

打开你的终端,执行下面这条命令,一个ES 8.12.0的单节点服务就跑起来了:

docker run -d \
  --name es8 \
  -p 9200:9200 \
  -p 9300:9300 \
  -e "discovery.type=single-node" \
  -e "xpack.security.enabled=false" \
  -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
  docker.elastic.co/elasticsearch/elasticsearch:8.12.0

我来解释一下这几个参数:-p是把容器内的9200和9300端口映射到宿主机,这样我们才能访问;discovery.type=single-node指定为单节点模式;xpack.security.enabled=false是暂时关闭安全认证,方便我们本地调试,生产环境一定要开启;最后一句是设置JVM堆内存,避免把电脑卡死。

启动后,在浏览器访问 http://localhost:9200,如果看到一串包含"you Know, for Search"的JSON信息,恭喜你,ES启动成功了!

光有ES还不够,我们还需要一个可视化工具来查看数据和调试查询,这就是Kibana。同样用Docker启动:

docker run -d \
  --name kibana8 \
  -p 5601:5601 \
  -e "ELASTICSEARCH_HOSTS=http://es8:9200" \
  --link es8:es8 \
  docker.elastic.co/kibana/kibana:8.12.0

这里--link参数把Kibana容器和刚才的ES容器连接起来。访问 http://localhost:5601,就能进入Kibana的界面了。在左侧菜单找到“Dev Tools”,这里可以像在IDE里一样编写和执行ES的查询语句,非常方便。

2.2 创建SpringBoot 3.x项目并引入关键依赖

打开你喜欢的IDE(比如IntelliJ IDEA),创建一个新的SpringBoot项目。我习惯用Spring Initializr,选上这几个核心依赖:Spring WebLombok(简化实体类代码)、Spring Configuration Processor(让配置提示更友好)。注意,JDK版本要选17或以上,因为SpringBoot 3.x要求最低JDK 17。

项目创建好后,打开pom.xml文件,添加Elasticsearch 8.x官方Java客户端和其他必要的依赖。这里有个大坑要注意:ES 8.x的Java客户端和Spring Boot自带的spring-boot-starter-data-elasticsearch starter不兼容,后者内部还是用的老版本客户端。所以我们必须手动引入官方的elasticsearch-java

<dependency>
    <groupId>co.elastic.clients</groupId>
    <artifactId>elasticsearch-java</artifactId>
    <version>8.12.0</version> <!-- 版本号最好和你的ES服务保持一致 -->
</dependency>
<!-- 新客户端底层依赖Jackson和特定的JSON处理API -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
    <groupId>jakarta.json</groupId>
    <artifactId>jakarta.json-api</artifactId>
</dependency>
<!-- 可选:如果你需要更复杂的JSON处理,可以引入这个 -->
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-client</artifactId>
    <version>8.12.0</version>
</dependency>

依赖加好后,我建议你立刻运行一下mvn clean compile,确保没有依赖冲突。我遇到过好几次因为Jackson版本不对导致序列化出错的问题,提前检查能省下不少调试时间。

3. 配置与连接:打造健壮的ES客户端Bean

依赖搞定,接下来就是怎么让SpringBoot认识我们的ES服务了。这一步的核心是创建一个被Spring管理的ElasticsearchClient Bean。

3.1 在application.yml中灵活配置连接信息

我不喜欢把配置硬编码在Java代码里,所以先在application.yml(或application.properties)里定义好连接参数。这里我支持配置多个ES节点地址,用逗号分隔,为以后接入集群留好扩展性。

spring:
  elasticsearch:
    # 支持单节点或集群,多个地址用逗号分隔,例如:192.168.1.100:9200,192.168.1.101:9200
    uris: localhost:9200
    # 如果ES开启了安全认证(生产环境必须),在这里配置用户名密码
    username: elastic
    password: your_password_here
    # 连接和读写超时设置,根据网络状况调整
    connection-timeout: 5000ms
    socket-timeout: 60000ms

3.2 编写配置类,注入功能完备的客户端

配置读进来了,现在写一个配置类来创建客户端Bean。这里面的代码稍微有点多,但每一行都有它的作用,我带你仔细看看。

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;

import javax.net.ssl.SSLContext;
import java.util.Arrays;

@Configuration
public class ElasticSearchConfig {

    @Value("${spring.elasticsearch.uris}")
    private String hosts;

    @Value("${spring.elasticsearch.username:}")
    private String userName;

    @Value("${spring.elasticsearch.password:}")
    private String passWord;

    @Bean(name = "elasticsearchClient")
    public ElasticsearchClient elasticsearchClient() {
        // 1. 将配置的字符串地址,转换为HttpHost数组
        HttpHost[] httpHosts = parseHosts();
        // 2. 构建最底层的RestClient Builder
        RestClientBuilder restClientBuilder = RestClient.builder(httpHosts);

        // 3. 配置请求超时时间(重要!避免慢查询拖死线程)
        restClientBuilder.setRequestConfigCallback(requestConfigBuilder ->
                requestConfigBuilder
                        .setConnectTimeout(5000) // 连接超时5秒
                        .setSocketTimeout(60000)  //  socket读写超时60秒
        );

        // 4. 配置HTTP客户端,这里主要处理认证
        restClientBuilder.setHttpClientConfigCallback(httpClientBuilder -> {
            HttpAsyncClientBuilder builderToReturn = httpClientBuilder;

            // 如果配置了用户名密码,就设置基础认证
            if (StringUtils.hasText(userName) && StringUtils.hasText(passWord)) {
                final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                credentialsProvider.setCredentials(
                        AuthScope.ANY,
                        new UsernamePasswordCredentials(userName, passWord)
                );
                builderToReturn.setDefaultCredentialsProvider(credentialsProvider);
            }

            // 如果你的ES集群使用了自签名证书,需要在这里配置SSLCo
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值