从七年开发视角带你吃透 Spring Boot 整合 Elasticsearch(附全流程实战 + 工具类)​

284 阅读12分钟

从七年开发视角带你吃透 Spring Boot 整合 Elasticsearch(附全流程实战 + 工具类)

在当今数字化时代,数据量呈爆发式增长,如何高效地存储、检索和分析这些数据成为了软件开发中的关键问题。Elasticsearch 作为一款强大的分布式搜索引擎,凭借其出色的全文搜索、实时分析和高扩展性等特性,在众多项目中得到了广泛应用。而 Spring Boot 作为 Java 开发领域中备受青睐的框架,以其快速开发、简化配置等优势,为开发者带来了极大的便利。将 Spring Boot 与 Elasticsearch 进行整合,能够充分发挥两者的优势,构建出高性能、可扩展的搜索应用。作为一名拥有七年开发经验的技术人员,在本文中,我将带你深入理解并掌握 Spring Boot 整合 Elasticsearch 的全过程,同时附上全流程实战以及实用的工具类代码示例,希望能帮助你在实际项目中更好地应用这一技术组合。

一、为什么选择 Spring Boot 和 Elasticsearch?

1.1 Spring Boot 的优势

Spring Boot 的出现,极大地简化了 Spring 应用的开发过程,成为众多开发者的首选框架。其核心优势主要体现在以下几个方面:

  • 自动配置:Spring Boot 能够根据项目中添加的依赖自动配置 Spring 应用程序。例如,当你在项目中引入spring-boot-starter-web依赖时,Spring Boot 会自动配置 Tomcat 和 Spring MVC,无需你手动进行繁琐的配置工作。这种自动配置机制大大减少了开发过程中的重复性劳动,让你能够更专注于业务逻辑的实现。
  • 独立运行:Spring Boot 应用程序可以打包成一个独立的 JAR 文件,其中包含了内嵌的 HTTP 服务器(如 Tomcat、Jetty 或 Undertow)。这意味着你无需将应用部署到外部服务器,只需运行 JAR 文件即可启动应用,极大地简化了应用的部署过程。无论是在开发环境、测试环境还是生产环境,都能轻松实现快速部署和运行。
  • 约定优于配置:Spring Boot 遵循 “约定优于配置” 的原则,通过提供一系列合理的默认配置,减少了开发者在配置上花费的时间和精力。你只需按照 Spring Boot 的约定进行项目结构的组织和代码的编写,就能快速搭建起一个功能完备的应用。当然,如果你有特殊的配置需求,也可以很方便地对默认配置进行覆盖和定制。
  • 丰富的插件生态:Spring Boot 拥有一个活跃的开源社区,提供了大量的插件和 “Starters” 来支持各种开发需求。你可以通过添加相应的 Starter 依赖,轻松集成各种技术,如数据库访问、安全认证、消息队列等。这些丰富的插件和 Starters 使得 Spring Boot 能够适应各种不同类型的项目需求,为开发者提供了极大的便利。

1.2 Elasticsearch 的特点

Elasticsearch 是一个基于 Lucene 的分布式搜索引擎,在大数据搜索和分析领域占据着重要地位。它具有以下显著特点:

  • 高性能的全文搜索:Elasticsearch 专注于全文搜索,能够快速地在海量数据中进行关键词匹配。它通过构建倒排索引,将文档中的每个单词映射到包含该单词的文档列表,从而实现高效的搜索。例如,在一个包含数百万篇文章的文档库中,使用 Elasticsearch 可以在毫秒级的时间内完成对特定关键词的搜索,并返回相关的文章列表。
  • 分布式架构与高扩展性:Elasticsearch 采用分布式架构,将数据分散存储在多个节点上。这种架构使得 Elasticsearch 具有良好的扩展性,你可以通过添加更多的节点来增加集群的处理能力和存储容量。同时,分布式架构还提供了高可用性,当某个节点出现故障时,系统能够自动将请求转移到其他正常节点,保证服务的连续性。
  • 实时分析与可视化:除了强大的搜索功能,Elasticsearch 还支持实时数据分析。它可以对数据进行聚合、统计和分析,并通过 Kibana 等工具进行可视化展示。这使得你能够快速地从大量数据中提取有价值的信息,为决策提供支持。例如,在电商领域,你可以使用 Elasticsearch 实时分析用户的购买行为、商品的销售趋势等数据,以便及时调整营销策略。
  • 多语言支持与 RESTful API:Elasticsearch 提供了丰富的客户端库,支持多种编程语言,如 Java、Python、JavaScript 等。同时,它还通过 RESTful API 对外提供服务,使得不同的应用程序都能够方便地与 Elasticsearch 进行交互。这为开发者在不同的技术栈中集成 Elasticsearch 提供了便利。

1.3 两者结合的优势

将 Spring Boot 与 Elasticsearch 集成,能够充分发挥两者的优势,为项目带来以下好处:

  • 快速开发与高效搜索:Spring Boot 的快速开发特性与 Elasticsearch 的高性能搜索能力相结合,能够大大缩短项目的开发周期,提高开发效率。你可以在 Spring Boot 项目中快速集成 Elasticsearch,实现强大的搜索功能,满足业务对搜索性能的要求。
  • 灵活的架构与可扩展性:Spring Boot 的微服务架构支持与 Elasticsearch 的分布式架构相得益彰,使得应用程序能够更好地适应不同规模的业务需求。无论是小型项目还是大型企业级应用,都可以通过合理配置 Spring Boot 和 Elasticsearch,实现灵活的架构设计和良好的可扩展性。
  • 丰富的功能与定制化:Spring Boot 提供了丰富的配置选项和扩展点,开发者可以根据项目的具体需求对应用程序进行定制化开发。而 Elasticsearch 的多种功能,如全文搜索、聚合分析、地理空间搜索等,能够满足不同业务场景下的搜索和分析需求。通过将两者结合,你可以打造出功能丰富、定制化程度高的搜索应用。

二、环境准备

在开始整合 Spring Boot 和 Elasticsearch 之前,需要确保以下环境已经准备就绪:

  • Java 环境:确保你的开发环境中已经安装了 Java Development Kit(JDK),并且版本不低于 1.8。你可以通过在命令行中输入java -version来检查 Java 版本。如果尚未安装 JDK,可以从 Oracle 官方网站或 OpenJDK 官网下载并安装。
  • Maven:Maven 是一个项目管理和构建工具,用于管理项目的依赖和构建过程。你可以从 Maven 官方网站下载并安装 Maven,安装完成后,通过在命令行中输入mvn -v来检查 Maven 是否安装成功。Maven 能够帮助我们方便地管理项目中的各种依赖,确保项目的构建和运行环境的一致性。
  • Elasticsearch:你需要安装并启动 Elasticsearch。可以从 Elasticsearch 官方网站下载适合你操作系统的安装包,然后按照官方文档的指引进行安装和配置。安装完成后,启动 Elasticsearch 服务,默认情况下,Elasticsearch 会监听在localhost:9200端口。你可以通过在浏览器中访问http://localhost:9200来检查 Elasticsearch 是否正常启动,如果看到类似于以下的 JSON 响应,表示 Elasticsearch 已经成功启动:
{
  "name" : "your-node-name",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "your-cluster-uuid",
  "version" : {
    "number" : "7.17.0",
    "build_flavor" : "default",
    "build_type" : "zip",
    "build_hash" : "your-build-hash",
    "build_date" : "2022-02-10T13:45:18.592000Z",
    "build_snapshot" : false,
    "lucene_version" : "8.11.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}
  • Elasticsearch Head(可选) :为了更方便地管理和查看 Elasticsearch 中的数据,你可以安装 Elasticsearch Head 插件。Elasticsearch Head 是一个基于 Web 的可视化工具,它提供了一个直观的界面来操作 Elasticsearch,如创建索引、查看文档、执行查询等。安装 Elasticsearch Head 需要先安装 Node.js 和 npm(Node Package Manager),然后通过 npm 安装 Elasticsearch Head。安装完成后,启动 Elasticsearch Head 服务,默认情况下,它会监听在localhost:9100端口。你可以通过在浏览器中访问http://localhost:9100来打开 Elasticsearch Head 界面。

三、Spring Boot 项目搭建

3.1 创建 Spring Boot 项目

我们可以使用 Spring Initializr 来快速创建一个 Spring Boot 项目。打开浏览器,访问https://start.spring.io/,在页面中进行如下配置:

  • Project:选择 Maven 项目。
  • Language:选择 Java。
  • Spring Boot:选择最新的稳定版本。
  • Group:填写项目的组织 ID,例如com.example。
  • Artifact:填写项目的名称,例如spring-boot-elasticsearch-demo。
  • Dependencies:在搜索框中搜索并添加Spring Data Elasticsearch和Spring Web依赖。Spring Data Elasticsearch依赖用于与 Elasticsearch 进行交互,Spring Web依赖用于创建 Web 服务,方便我们测试搜索功能。

配置完成后,点击页面下方的 “Generate” 按钮,Spring Initializr 会生成一个压缩包,将其下载并解压到本地。

3.2 导入项目到 IDE

将解压后的项目文件夹导入到你常用的 IDE 中,如 Eclipse 或 IntelliJ IDEA。以 IntelliJ IDEA 为例,打开 IntelliJ IDEA,选择 “File” -> “New” -> “Project from Existing Sources”,然后选择解压后的项目文件夹,按照向导提示完成项目导入。

3.3 项目结构介绍

导入项目后,我们来看一下项目的基本结构:

spring-boot-elasticsearch-demo
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── example
│   │   │           ├── SpringBootElasticsearchDemoApplication.java
│   │   │           ├── controller
│   │   │           │   └── ProductController.java
│   │   │           ├── service
│   │   │           │   └── ProductService.java
│   │   │           └── repository
│   │   │               └── ProductRepository.java
│   │   └── resources
│   │       ├── application.properties
│   │       ├── static
│   │       └── templates
│   └── test
│       ├── java
│       │   └── com
│       │       └── example
│       │           └── SpringBootElasticsearchDemoApplicationTests.java
│       └── resources
├── pom.xml
└── README.md
  • src/main/java:存放项目的 Java 源代码。在这个目录下,我们按照功能模块划分了不同的包,如controller包用于存放控制器类,处理 HTTP 请求;service包用于存放业务逻辑类;repository包用于存放与 Elasticsearch 交互的数据访问类。
  • src/main/resources:存放项目的资源文件,如配置文件、静态资源、模板文件等。application.properties文件用于配置项目的各种属性,包括 Elasticsearch 的连接信息等。
  • src/test:存放项目的测试代码。在测试代码中,我们可以对各个功能模块进行单元测试和集成测试,确保项目的质量。
  • pom.xml:Maven 项目的核心配置文件,用于管理项目的依赖和构建过程。在pom.xml文件中,我们可以添加、删除和管理项目所依赖的各种库和插件。

四、配置 Elasticsearch 连接

在src/main/resources目录下的application.properties文件中添加 Elasticsearch 的连接配置:

spring.data.elasticsearch.cluster-name=elasticsearch
spring.data.elasticsearch.cluster-nodes=localhost:9300
spring.data.elasticsearch.username=elastic
spring.data.elasticsearch.password=password

上述配置中,spring.data.elasticsearch.cluster-name指定了 Elasticsearch 集群的名称,默认情况下为elasticsearch。spring.data.elasticsearch.cluster-nodes指定了 Elasticsearch 集群节点的地址和端口,这里假设 Elasticsearch 运行在本地,端口为 9300。spring.data.elasticsearch.username和spring.data.elasticsearch.password分别指定了连接 Elasticsearch 的用户名和密码,如果你的 Elasticsearch 启用了安全认证,需要填写正确的用户名和密码。请根据你的实际情况进行修改。

五、创建实体类

在src/main/java/com/example目录下创建一个entity包,并在该包下创建一个Product实体类,用于映射 Elasticsearch 中的文档:

package com.example.entity;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@Document(indexName = "products", shards = 1, replicas = 0)
public class Product {
    @Id
    private String id;
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String name;
    @Field(type = FieldType.Double)
    private Double price;
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String description;
    // 省略getter和setter方法
}

在上述代码中,@Document注解用于指定该实体类对应的 Elasticsearch 索引名称为products,并设置了索引的分片数为 1,副本数为 0。@Id注解用于标识该字段为文档的主键。@Field注解用于指定字段的类型和分词器等属性。例如,name和description字段的类型为Text,并使用了ik_max_word分词器,ik_max_word分词器是一个中文分词器,能够将中文文本进行更细粒度的分词,提高搜索的准确性。price字段的类型为Double,用于存储产品的价格。

六、创建 Repository 接口

在src/main/java/com/example/repository目录下创建一个ProductRepository接口,继承自ElasticsearchRepository,用于对Product实体进行数据库操作:

package com.example.repository;
import com.example.entity.Product;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface ProductRepository extends ElasticsearchRepository<Product, String> {
    // 可以自定义查询方法,例如根据产品名称查询产品
    Product findByName(String name);
}

ElasticsearchRepository接口提供了一系列默认的 CRUD 操作方法,如save、findAll、findById、deleteById等。同时,我们还可以根据方法名自动生成查询语句,例如上述代码中的findByName方法。通过继承ElasticsearchRepository接口,我们可以方便地对Product实体进行各种数据库操作,而无需编写大量的 SQL 语句。

七、创建 Service 层

在src/main/java/com/example/service目录下创建一个ProductService类,用于封装业务逻辑:

package com.example.service;
import com.example.entity.Product;
import com.example.repository.ProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class ProductService {
    @Autowired
    private ProductRepository productRepository;
    public Product saveProduct(Product product) {
        return productRepository.save(product);
    }
    public Optional<Product> findProductById(String id) {
        return productRepository.findById(id);
    }
    public List<Product> findAllProducts() {
        return productRepository.findAll();
    }
    public void deleteProductById(String id) {
        productRepository.deleteById(id);
    }
    public Product findProductByName(String name) {
        return productRepository.findByName(name);
    }
}

在上述代码中,通过@Autowired注解将ProductRepository注入到ProductService中,然后在各个业务方法中调用ProductRepository的方法来进行数据库操作。例如,saveProduct方法用于保存产品信息,findProductById方法用于根据产品 ID 查询产品信息,findAllProducts方法用于查询所有产品信息,deleteProductById方法用于根据产品 ID 删除产品信息,findProductByName方法用于根据产品名称查询产品信息。通过将业务逻辑封装在ProductService类中,可以提高代码的可维护性和可扩展性。

八、创建 Controller 层

在src/main/java/com/example/controller目录下创建一个ProductController类,用于提供 RESTful 接口:

package com.example.controller;
import com.example.entity.Product;
import com.example.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/products")
public class ProductController {
    @Autowired
    private ProductService productService;
    @PostMapping
    public ResponseEntity<Product> createProduct(@RequestBody Product product) {
        Product savedProduct = productService.saveProduct(product);
        return new ResponseEntity<>(savedProduct, HttpStatus.CREATED);
    }