简介从文档 Spring Data Elasticsearch - Reference Documentation 可以查看到Spring 与 Elasticsearch之间的版本关系。这里由于项目的需要，我们选取的是 Neumann版本，对应的是Spring Data Elasticsearch 4.0.xElasticsearch 7.6.2Spring Framework 5.2.12Spring Boot 2.3.x官方文档 Spring Data Neumann goes GA，注意需要使用的是这个文档。Elasticsearch 和 数据库 对比SQL数据库Elasticsearchcolumn(列)field(字段)row(行)document(文档)table(表)index(索引)schema(模式)mapping(映射)database server(数据库服务器)Elasticsearch 集群实例测试方法我们使用如下测试代码作为测试基础package com.example.demo; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class ElasticsearchTests { @Autowired private ElasticsearchRestTemplate elasticsearchRestTemplate; @Test void testQuery() { // 测试代码 } }只需要修改testQuery内代码，就可以完成针对不同代码的测试。查询所有文档查询一个索引中的所有数据。类似于查询语句select * from metric_server;接下来是获取Elasticsearch数据@Data class TestObject { private String name; private String region; private String resourceId; } @Test void testQuery() { Query query = new NativeSearchQueryBuilder() .withQuery(matchAllQuery()) .build(); IndexCoordinates indexCoordinates = IndexCoordinates.of("metric_server"); List<TestObject> objs = elasticsearchRestTemplate.queryForList(query, TestObject.class, indexCoordinates); for ( int i = 0 ; i < objs.size() ;i++) { System.out.println(objs.get(i)); } }根据条件简单查询加上条件再做查询,类似于以下select * from metric_server where name = 'agenttest';代码也比较简单，使用termQuery来匹配@Test void testQuery() { TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "agenttest"); Query query = new NativeSearchQueryBuilder() .withQuery(termQueryBuilder) .build(); IndexCoordinates indexCoordinates = IndexCoordinates.of("metric_server"); List<TestObject> objs = elasticsearchRestTemplate.queryForList(query, TestObject.class, indexCoordinates); for ( int i = 0 ; i < objs.size() ;i++) { System.out.println(objs.get(i)); } }各匹配类型说明Query类型说明matchQuery分词，然后调用termQuerytermQuery不分词，严格匹配wildcardQuery通配符匹配fuzzyQuery模糊匹配rangeQuery范围匹配booleanQuery布尔匹配(几个匹配之间做布尔运算)根据条件做复杂查询做一个比较复杂的查询select * from metric_server where name = 'agenttest' and timestamp > '2021-02-10 00:00:00'同样的，我们可以使用下面代码做测试@Test void testQuery() { long sinceTime = System.currentTimeMillis() - 1000L*60*60;; BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); boolQueryBuilder.must(termQuery("name", "agenttest")); boolQueryBuilder.must(QueryBuilders.rangeQuery("timestamp").gte(sinceTime)); // boolQueryBuilder.filter(QueryBuilders.rangeQuery("timestamp").gte(sinceTime)); // 同上 Query query = new NativeSearchQueryBuilder() .withQuery(boolQueryBuilder) .build(); IndexCoordinates indexCoordinates = IndexCoordinates.of("metric_server"); List<TestObject> objs = elasticsearchRestTemplate.queryForList(query, TestObject.class, indexCoordinates); for ( int i = 0 ; i < objs.size() ;i++) { System.out.println(objs.get(i)); } }运行如下图其中filter和must在这里是一致的，但是他们在ES里还是有差别的，详细参考ElasticSearch中must和filter的区别Es检索 must与filter区别查询并排序查询并测试select * from metric_server where name = 'agenttest' order by timestamp desc;使用下面代码测试@Data class TestObject { private String name; private String region; private String resourceId; private String timestamp; } @Test void testQuery() { TermQueryBuilder termQuery = QueryBuilders.termQuery("name", "agenttest"); SortBuilder sortBuilder = SortBuilders.fieldSort("timestamp").order(SortOrder.DESC); Query query = new NativeSearchQueryBuilder() .withQuery(termQuery) .withSort(sortBuilder) .build(); IndexCoordinates indexCoordinates = IndexCoordinates.of("metric_server"); List<TestObject> objs = elasticsearchRestTemplate.queryForList(query, TestObject.class, indexCoordinates); System.out.println(objs.size()); for ( int i = 0 ; i < objs.size() ;i++) { System.out.println(objs.get(i)); } }查看结果参考springboot中ElasticSearch入门与进阶：组合查询、聚合查询Spring Data Elasticsearch使用【必看】SpringBoot整合Spring Data ElasticsearchElasticSearch AggregationBuilders java api常用聚会查询ElasticSearch-AggregationBuilders查询遇到的坑Elasticsearch Aggregation 笔记Spring Data Elasticsearch
SpringBoot启动流程Spring启动流程，最重要的就是这个函数。我们也是从这里开始入手。SpringApplication.run(DemoApplication.class, args);在网上，我找到一个比较不错的分析图启动代码主要分成2部分。构造SpringApplication执行run方法构造SpringApplication构造函数包括一系列重载函数，最后调用下面这个函数@SuppressWarnings({ "unchecked", "rawtypes" }) public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // #1 侦测web application type this.webApplicationType = WebApplicationType.deduceFromClasspath(); // #2 获取bootstrap registry initializers this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories(); // #3 // 获取 ApplicationContextInitializer 设置initializers // 获取 ApplicationListener 设置listeners setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // #4 获取main函数所在类 this.mainApplicationClass = deduceMainApplicationClass(); }1 侦测web application type有三种类型的web application typeNONESERVLETREACTIVE2 获取bootstrap registry initializersBootstrapper 同时调用初始化3 设置initializers和listener修改一下代码，方便调试的时候可以直接查看变量setInitializers只是简单的设置，不赘述public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) { this.initializers = new ArrayList<>(initializers); }listeners同理获取实例的方法如下，我们跟进去看下代码易得private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates // 从每个包的META-INF/spring.factories处获取实现了对应接口的class Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 利用反射实例化！ List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 根据order权重排序，初始化是有顺序的，数字越小越靠前 AnnotationAwareOrderComparator.sort(instances); return instances; }4 获取main函数所在类this.mainApplicationClass = deduceMainApplicationClass();这一步，我们把定义的DemoApplication侦测到参考SpringBoot启动原理
参考文章Idea导入SpringBoot源码终极版(基于Gradle)IDEA、JDK 与GradleIntelliJ IDEA使用sdkman安装JDK11,注意安装的版本是11.0.12-open，不要使用11.0.11.j9-adpt使用sdkman安装Gradle源码准备如果是linux上，比如我用的mint，直接可以下载拉取代码git clone https://github.com/spring-projects/spring-boot.git如果在windows上，请注意，你有可能会碰到Filename too long的报错，这种情况下，如果使用win8以上系统，你可以如下设置,参考git config --global core.longpaths true切换到对应v2.5.7 taggit tag v2.5.7添加测试模块(参考上面文章)新建模块名字改为spring-boot-debug修改build.gradle文件，把java插件改成applicationplugins { id 'application' } group 'org.springframework.boot' version '2.5.7' repositories { mavenCentral() } dependencies { annotationProcessor("org.projectlombok:lombok:1.18.22") annotationProcessor(project(":spring-boot-project:spring-boot-tools:spring-boot-configuration-processor")) implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web")) implementation(enforcedPlatform(project(":spring-boot-project:spring-boot-dependencies"))) implementation(project(":spring-boot-project:spring-boot")) implementation("jakarta.validation:jakarta.validation-api") implementation("org.projectlombok:lombok:1.18.22") } application { mainClass="org.springframework.debug.DemoApplication" }DemoApplication.java代码package org.springframework.debug; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }现在可以在模块中随便写一些测试代码同时，我们给对SpringApplication.java代码Banner代码上下打印出标识符号，如图所示。运行这个application，如图输出则表示成功