基于 SpringBoot 2.7.x 使用最新的 Elasticsearch Java API Client 之 ElasticsearchClient

1. 从 RestHighLevelClient 到 ElasticsearchClient

从 Java Rest Client 7.15.0 版本开始,Elasticsearch 官方决定将 RestHighLevelClient 标记为废弃的,并推荐使用新的 Java API Client,即 ElasticsearchClient. 为什么要将 RestHighLevelClient 废弃,大概有以下几点:

  • 维护成本高:RestHighLevelClient 需要和 Elasticsearch APIs 的更新保持一致,而 Elasticsearch APIs 更新较为频繁,因此每次 Elasticsearch APIs 有新的迭代,RestHighLevelClient 也要跟着迭代,维护成本高。
  • 兼容性差: 由于 RestHighLevelClient 和 Elasticsearch 的内部数据结构紧耦合,而 Elasticsearch 不同版本的数据结构可能略有差异,因此很难跨不同版本的 Elasticsearch 保持兼容。
  • 灵活度低: RestHighLevelClient 的灵活性扩展性较差,很难去扩展或者自定义一些功能。

而 Spring 官方对 Elasticsearch 客户端也进行了封装,集成于 spring-boot-starter-data-elasticsearch 模块,Elasticsearch 官方决定废弃 RestHighLevelClient 而支持 ElasticsearchClient 这一举措,必然也导致 Spring 项目组对 data-elasticserach 模块进行同步更新,以下是 Spring 成员对相关内容的讨论:

  • https://github.com/spring-projects/spring-boot/issues/28597

大概内容就是在对 ElasticsearchClient 自动装配的支持会在 springboot 3.0.x 版本中体现,而在 2.7.x 版本中会将 RestHighLevelClient 标记为废弃的。

由于我们的项目是基于 springboot 2.7.10 版本开发的,而 2.7.x 作为最后一个 2.x 版本,springboot 下个版本为 3.x,恰逢项目已经规划在半年后将 JDK 升级为17版本,全面支持 springboot 3.x 版本的替换,因此现阶段需要封装一个能够跨 2.7.x 和 3.x 版本都可以使用的 Elasticsearch 客户端。

2. 自定义 starter 模块实现 ElasticsearchTemplate 的自动装配

在调研了 spring-boot 2.7.10 版本的源码后发现,其实 2.7.x 版本已经引入了 ElasticsearchClient,并封装了新的客户端 ElasticsearchTemplate,但是并没有为其做自动装配,如果想要使用基于ElasticsearchClient 的 ElasticsearchTemplate,需要用户自己装配。否则,直接使用 ElasticsearchTemplate 会出现以下提示:

Consider defining a bean of type 'org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate' in your configuration.

即由提示可以知道,无法创建一个 ElasticsearchTemplate 类型的 bean.

因此需要自己实现 ElasticsearchTemplate 的装配,才可以使用。为了能够一次装配多项目复用,决定自己构建一个starter,之后需要使用 ElasticsearchTemplate,可以通过引入依赖的方式完成自动装配。

自定义的 starter 项目目录结构如下图所示:
在这里插入图片描述

pom.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><description>自定义elasticsearch-client组件</description><parent><artifactId>xxx-spring-boot-starters</artifactId><groupId>com.xxx.commons</groupId><version>${revision}</version></parent><artifactId>xxx-elasticsearch-client-spring-boot-starter</artifactId><packaging>jar</packaging><name>xxx-elasticsearch-client-spring-boot-starter</name><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId><exclusions><exclusion><groupId>jakarta.json</groupId><artifactId>jakarta.json-api</artifactId></exclusion></exclusions></dependency><dependency><groupId>jakarta.json</groupId><artifactId>jakarta.json-api</artifactId><version>2.0.1</version></dependency></dependencies>
</project>

org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件

com.xxx.commons.springboot.elasticsearch.ElasticsearchTemplateAutoConfiguration
com.xxx.commons.springboot.elasticsearch.actuate.xxxElasticsearchHealthIndicatorAutoConfiguration

PackageInfo 接口:

package com.xxx.commons.springboot.elasticsearch;/*** @author reader* Date: 2023/9/18 22:21**/
public interface PackageInfo {
}

RestClientBuilder 类:

package com.xxx.commons.springboot.elasticsearch;import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponseInterceptor;
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.message.BasicHeader;
import org.elasticsearch.client.RestClient;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties;import java.net.URI;
import java.net.URISyntaxException;/*** @author reader* Date: 2023/9/20 15:16**/
public final class RestClientBuilder {private RestClientBuilder() {}public static RestClient buildWithProperties(ElasticsearchProperties properties) {HttpHost[] hosts = properties.getUris().stream().map(RestClientBuilder::createHttpHost).toArray((x$0) -> new HttpHost[x$0]);org.elasticsearch.client.RestClientBuilder builder = RestClient.builder(hosts);builder.setDefaultHeaders(new BasicHeader[]{new BasicHeader("Content-type", "application/json")});builder.setHttpClientConfigCallback((httpClientBuilder) -> {httpClientBuilder.addInterceptorLast((HttpResponseInterceptor) (response, context) -> response.addHeader("X-Elastic-Product", "Elasticsearch"));if (hasCredentials(properties.getUsername(), properties.getPassword())) {// 密码配置CredentialsProvider credentialsProvider = new BasicCredentialsProvider();credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(properties.getUsername(), properties.getPassword()));httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);}// httpClient配置return httpClientBuilder;});builder.setRequestConfigCallback((requestConfigBuilder) -> {// request配置requestConfigBuilder.setConnectionRequestTimeout((int)properties.getConnectionTimeout().getSeconds() * 1000);requestConfigBuilder.setSocketTimeout((int)properties.getSocketTimeout().getSeconds() * 1000);return requestConfigBuilder;});if (properties.getPathPrefix() != null) {builder.setPathPrefix(properties.getPathPrefix());}return builder.build();}private static boolean hasCredentials(String username, String password) {return StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password);}private static HttpHost createHttpHost(String uri) {try {return createHttpHost(URI.create(uri));} catch (IllegalArgumentException var2) {return HttpHost.create(uri);}}private static HttpHost createHttpHost(URI uri) {if (StringUtils.isBlank(uri.getUserInfo())) {return HttpHost.create(uri.toString());} else {try {return HttpHost.create((new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment())).toString());} catch (URISyntaxException var2) {throw new IllegalStateException(var2);}}}
}

ElasticsearchClientConfiguration 类:

package com.xxx.commons.springboot.elasticsearch;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.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.elasticsearch.client.RestClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @author reader* Date: 2023/9/20 14:59**/
@Configuration
@EnableConfigurationProperties({ElasticsearchProperties.class})
@ConditionalOnClass({ElasticsearchClient.class, ElasticsearchTransport.class})
public class ElasticsearchClientConfiguration {protected static final Log LOGGER = LogFactory.getLog(ElasticsearchClientConfiguration.class);private ElasticsearchProperties elasticsearchProperties;public ElasticsearchClientConfiguration(ElasticsearchProperties elasticsearchProperties) {LOGGER.info("框架 elasticsearch-client-starter elasticsearchProperties 装载开始");this.elasticsearchProperties = elasticsearchProperties;}@Beanpublic ElasticsearchClient elasticsearchClient() {LOGGER.info("框架 elasticsearch-client-starter elasticsearchClient 装载开始");RestClient restClient = RestClientBuilder.buildWithProperties(elasticsearchProperties);RestClientTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());return new ElasticsearchClient(transport);}
}
package com.xxx.commons.springboot.elasticsearch;import co.elastic.clients.elasticsearch.ElasticsearchClient;
import com.xxx.commons.springboot.elasticsearch.actuate.ElasticsearchInfoContributor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.info.ConditionalOnEnabledInfoContributor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScanner;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions;
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;/*** @author reader* Date: 2023/9/19 16:35**/
@AutoConfiguration(before = {ElasticsearchRestClientAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class})
@ConditionalOnClass({ElasticsearchTemplate.class})
@EnableConfigurationProperties({ElasticsearchProperties.class})
@Import({ElasticsearchClientConfiguration.class})
public class ElasticsearchTemplateAutoConfiguration {protected static final Log LOGGER = LogFactory.getLog(ElasticsearchTemplateAutoConfiguration.class);@BeanElasticsearchCustomConversions elasticsearchCustomConversions() {return new ElasticsearchCustomConversions(Collections.emptyList());}@Beanpublic SimpleElasticsearchMappingContext elasticsearchMappingContext(ApplicationContext applicationContext,ElasticsearchCustomConversions elasticsearchCustomConversions) throws ClassNotFoundException {SimpleElasticsearchMappingContext mappingContext = new SimpleElasticsearchMappingContext();mappingContext.setInitialEntitySet(new EntityScanner(applicationContext).scan(Document.class));mappingContext.setSimpleTypeHolder(elasticsearchCustomConversions.getSimpleTypeHolder());return mappingContext;}@BeanElasticsearchConverter elasticsearchConverter(SimpleElasticsearchMappingContext mappingContext,ElasticsearchCustomConversions elasticsearchCustomConversions) {MappingElasticsearchConverter converter = new MappingElasticsearchConverter(mappingContext);converter.setConversions(elasticsearchCustomConversions);return converter;}@BeanElasticsearchTemplate elasticsearchTemplate(ElasticsearchClient client, ElasticsearchConverter converter) {LOGGER.info("框架 elasticsearch-client-starter elasticsearchTemplate 装载开始");return new ElasticsearchTemplate(client, converter);}@Bean@ConditionalOnEnabledInfoContributor("elasticsearch")public ElasticsearchInfoContributor elasticsearchInfoContributor(ObjectProvider<ElasticsearchProperties> propertiesObjectProvider) {List<ElasticsearchProperties> properties = new ArrayList<>();propertiesObjectProvider.forEach(properties::add);return new ElasticsearchInfoContributor(properties);}
}

健康度指标相关的封装有:

  • ElasticsearchHealthIndicator 类:
package com.xxx.commons.springboot.elasticsearch.actuate;import org.elasticsearch.client.Node;
import org.elasticsearch.client.RestClient;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;/*** @author reader* Date: 2023/9/20 19:26**/
public class ElasticsearchHealthIndicator extends AbstractHealthIndicator {private final List<RestClient> clients;public ElasticsearchHealthIndicator(List<RestClient> clients) {this.clients = clients;}@Overrideprotected void doHealthCheck(Health.Builder builder) throws Exception {boolean success = true;Map<String, Object> properties = new HashMap<>();for (RestClient client : clients) {List<Node> nodes = client.getNodes();if (null == nodes || nodes.isEmpty()){continue;}String id = nodes.stream().map(Node::toString).collect(Collectors.joining(";"));boolean ps = client.isRunning();properties.put("ElasticsearchClient[" + id + "]", ps);if (!ps) {success = false;}}if (success) {builder.up();} else {builder.withDetails(properties).down();}}
}
  • ElasticsearchInfoContributor 类:
package com.xxx.commons.springboot.elasticsearch.actuate;import com.xxx.commons.springboot.elasticsearch.PackageInfo;
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties;import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author reader* Date: 2023/9/20 19:32**/
public class ElasticsearchInfoContributor implements InfoContributor {private final List<ElasticsearchProperties> elasticsearchProperties;public ElasticsearchInfoContributor(List<ElasticsearchProperties> elasticsearchProperties) {this.elasticsearchProperties = elasticsearchProperties;}@Overridepublic void contribute(Info.Builder builder) {Map<String, Object> properties = new HashMap<>();properties.put("version", PackageInfo.class.getPackage().getImplementationVersion());properties.put("_title_", "ElasticsearchTemplate组件");elasticsearchProperties.forEach(p -> {Map<String, Object> sp = new HashMap<>();String id = String.join(";", p.getUris());properties.put(id, sp);sp.put("nodes", String.join(";", p.getUris()));sp.put("user", p.getUsername());sp.put("connectionTimeout[ms]", p.getConnectionTimeout().toMillis());sp.put("socketTimeout[ms]", p.getSocketTimeout().toMillis());});builder.withDetail("xxx-elasticsearch-client", properties);}
}
  • xxxElasticsearchHealthIndicatorAutoConfiguration 类:
package com.xxx.commons.springboot.elasticsearch.actuate;import org.elasticsearch.client.RestClient;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;import java.util.ArrayList;
import java.util.List;/*** @author reader* Date: 2023/9/20 19:45**/
@AutoConfiguration(before = {HealthContributorAutoConfiguration.class})
@ConditionalOnEnabledHealthIndicator("elasticsearch")
public class xxxElasticsearchHealthIndicatorAutoConfiguration {@Bean("elasticsearchHealthIndicator")@ConditionalOnMissingBeanpublic ElasticsearchHealthIndicator xxxElasticHealthIndicator(ObjectProvider<RestClient> elasticsearchClientProvider) {List<RestClient> restClients = new ArrayList<>();elasticsearchClientProvider.forEach(restClients::add);return new ElasticsearchHealthIndicator(restClients);}
}

3. 使用自定义的 starter

1、在自己封装了一个 starter 工具模块之后,通过引入依赖的方式使用,引入的依赖为:

<dependency><groupId>com.xxx.commons</groupId><artifactId>xxx-elasticsearch-client-spring-boot-starter</artifactId><version>${version}</version>
</dependency>

在 yaml 文件中配置的相关属性信息:

spring:elasticsearch:uris: http://127.0.0.1:9200         username: elasticpassword: password

注入并使用 ElasticsearchTemplate 对 ES 进行操作:

package com.xxx.xxx;import com.xxx.commons.result.query.PaginationBuilder;
import com.xxx.commons.result.query.Query;
import com.xxx.commons.result.query.QueryBuilder;
import com.xxx.push.domain.AliPushRecordDO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.CollectionUtils;import java.util.ArrayList;
import java.util.List;/*** @author reader* Date: 2023/9/26 14:42**/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, properties = {"profile=dev", "debug=true"})
public class ElasticsearchTemplateTest {@Autowiredprivate ElasticsearchTemplate elasticsearchTemplate;@Testpublic void testSearch() {Query query = QueryBuilder.page(1).pageSize(20).build();NativeQueryBuilder nativeQueryBuilder = new NativeQueryBuilder();nativeQueryBuilder.withPageable(PageRequest.of(query.getPage() - 1, query.getPageSize()));NativeQuery searchQuery = nativeQueryBuilder.build();// 查询总数long count = elasticsearchTemplate.count(searchQuery, AliPushRecordDO.class);PaginationBuilder<AliPushRecordDO> builder = PaginationBuilder.query(query);builder.amount((int) count);if (count > 0) {SearchHits<AliPushRecordDO> aliPushRecordDOSearchHits = elasticsearchTemplate.search(searchQuery, AliPushRecordDO.class);List<SearchHit<AliPushRecordDO>> searchHits = aliPushRecordDOSearchHits.getSearchHits();List<AliPushRecordDO> aliPushRecordDOList = new ArrayList<>();if (!CollectionUtils.isEmpty(searchHits)) {searchHits.forEach(searchHit -> aliPushRecordDOList.add(searchHit.getContent()));}builder.result(aliPushRecordDOList);} else {builder.result(new ArrayList<>());}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/148315.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C语言】内存函数的详细教学和模拟实现

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是gugugu。希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f194;本文由 gugugu 原创 CSDN首发&#x1f412; 如需转载还请通知⚠…

【再识C进阶4】详细介绍自定义类型——结构体、枚举和联合

学习目标&#xff1a; 在上一篇博客中&#xff0c;我们已经详细地学习了字符分类函数、字符转换函数和内存函数。那这一篇博客和上一篇博客的关系不是那么相连。 这一篇博客主要介绍一下自定义类型&#xff0c;因为在解决实际问题时&#xff0c;由于世界上的因素有很多&#xf…

karmada v1.7.0安装指导

前言 安装心得 经过多种方式操作&#xff0c;发现二进制方法安装太复杂&#xff0c;证书生成及其手工操作太多了&#xff0c;没有安装成功&#xff1b;helm方式的安装&#xff0c;v1.7.0的chart包执行安装会报错&#xff0c;手工修复了报错并修改了镜像地址&#xff0c;还是各…

一文拿捏Spring事务之、ACID、隔离级别、失效场景

1.&#x1f31f;Spring事务 1.编程式事务 事务管理代码嵌入嵌入到业务代码中&#xff0c;来控制事务的提交和回滚&#xff0c;例如TransactionManager 2.声明式事务 使用aop对方法前后进行拦截&#xff0c;然后在目标方法开始之前创建或者加入一个事务&#xff0c;执行完目…

一键AI高清换脸——基于InsightFace、CodeFormer实现高清换脸与验证换脸后效果能否通过人脸比对、人脸识别算法

前言 AI换脸是指利用基于深度学习和计算机视觉来替换或合成图像或视频中的人脸。可以将一个人的脸替换为另一个人的脸,或者将一个人的表情合成到另一个人的照片或视频中。算法常常被用在娱乐目上,例如在社交媒体上创建有趣的照片或视频,也有用于电影制作、特效制作、人脸编…

MySQL5.7版本与8.0版本在CentOS系统安装

目录 前置要求 1. MySQL5.7版本在CentOS系统安装 1.1 安装 1.1.1 配置yum仓库 1.1.2 使用yum安装MySQL 1.1.3 安装完成后&#xff0c;启动MySQL并配置开机自启动 1.1.4 检查MySQL的运行状态 1.2 配置 1.2.1 获取MySQL的初始密码 1.2.2 登陆MySQL数据库系统 …

《Secure Analytics-Federated Learning and Secure Aggregation》论文阅读

背景 机器学习模型对数据的分析具有很大的优势&#xff0c;很多敏感数据分布在用户各自的终端。若大规模收集用户的敏感数据具有泄露的风险。 对于安全分析的一般背景就是认为有n方有敏感数据&#xff0c;并且不愿意分享他们的数据&#xff0c;但可以分享聚合计算后的结果。 联…

Python无废话-办公自动化Excel图表制作

openpyxl 支持用Excel工作表中单元格的数据&#xff0c;创建条形图、折线图、散点图和饼图等。 图表制作步骤 在openpyxl模块中创建图表&#xff0c;步骤如下: ①选择一个单元格区域&#xff0c;创建Reference 对象&#xff0c;作为图形数据a)(Value)。 ②创建一个Chart对象…

简单查找重复文本文件

声明这是最初 我的提问给个文本分类清单input查找文件夹下 .py .txt .excel .word 一模一样的文本不是找文件名 找相同格式下的文件文本是否一样 文件单独复制到文件夹下两个文件全部复制到文件夹下 print 打印相同文本文件的名字 比如查找到了3.py与4.5.是.py文件中的文本文件…

AtCoder Beginner Contest 233 (A-Ex)

A.根据题意模拟即可 B.根据题意模拟即可 C.直接用map 进行dp即可 D.用前缀和进行模拟&#xff0c;用map统计前缀和&#xff0c;每次计算当前前缀和-k的个数就是以当前点为右端点答案。 E - Σ[k0..10^100]floor(X&#xff0f;10^k) (atcoder.jp) &#xff08;1&#xff09;…

blender光照系统设置

0&#xff09;Viewport Shading设置里面的Lighting下面的参数&#xff1a; Scene Lights,Scene World - Scene Lights是指在渲染模式下是否使用场景中的灯光对象来照亮物体。 - Scene World是指在渲染模式下是否使用场景中的世界设置来作为背景和环境光。如果关闭该选项&#…

分类预测 | MATLAB实现NGO-CNN北方苍鹰算法优化卷积神经网络数据分类预测

分类预测 | MATLAB实现NGO-CNN北方苍鹰算法优化卷积神经网络数据分类预测 目录 分类预测 | MATLAB实现NGO-CNN北方苍鹰算法优化卷积神经网络数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现NGO-CNN北方苍鹰算法优化卷积神经网络数据分类预测&…

springmvc中DispatcherServlet关键对象

以下代码为 spring boot 2.7.15 中自带的 spring 5.3.29 RequestMappingInfo 请求方法相关信息封装&#xff0c;对应的信息解析在 RequestMappingHandlerMapping 的 createRequestMappingInfo() 中实现。 对于 RequestMapping 赋值的相关信息进行解析 protected RequestMappi…

【AI视野·今日NLP 自然语言处理论文速览 第四十六期】Tue, 3 Oct 2023

AI视野今日CS.NLP 自然语言处理论文速览 Tue, 3 Oct 2023 (showing first 100 of 110 entries) Totally 100 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Its MBR All the Way Down: Modern Generation Techniques Through the …

竞赛选题 机器视觉的试卷批改系统 - opencv python 视觉识别

文章目录 0 简介1 项目背景2 项目目的3 系统设计3.1 目标对象3.2 系统架构3.3 软件设计方案 4 图像预处理4.1 灰度二值化4.2 形态学处理4.3 算式提取4.4 倾斜校正4.5 字符分割 5 字符识别5.1 支持向量机原理5.2 基于SVM的字符识别5.3 SVM算法实现 6 算法测试7 系统实现8 最后 0…

Ubuntu22.04 交叉编译gcc9.5 for arm

一、准备 环境&#xff1a;ubuntu22.04为刚刚安装&#xff0c;未安装gcc等包 vi ~/.bashrc输入 export PATH$PATH:/opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin 保存,reboot 安装&#xff1a; sudo apt install cmake sudo apt install gawk sudo apt instal…

STM32复习笔记(五):FSMC连接外部SRAM

目录 Preface&#xff1a; &#xff08;一&#xff09;原理相关 &#xff08;二&#xff09;CUBEMX配置 &#xff08;三&#xff09;轮询方式读写 &#xff08;四&#xff09;DMA方式读写 Preface&#xff1a; STM32F4有一个FSMC&#xff08;Flexible Static Memory Contr…

mysql面试题11:讲一讲MySQL主从复制模式

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:讲一讲MySQL主从复制模式? MySQL主从复制的配置步骤如下: 在主服务器上配置: 打开主服务器的配置文件my.cnf,启用二进制日志(binary log)功…

ElementUI - 主页面--动态树右侧内容管理

一.左侧动态树 1.定义组件 ①样式&数据处理 <template><el-menu class"el-menu-vertical-demo" background-color"#334157"text-color"#fff" active-text-color"#ffd04b" :collapse"collapsed" router :def…

以太网基础学习(一)——以太网概述

一、以太网概述 以太网(Ethernet)指的是由 Xerox公司创建并由Xerox、Intel和 DEC公司联合开发的基带局域网规范&#xff0c;通用的以太网标准于1980年9月30日出台&#xff0c;是当今现有局域网采用的最通用的通信协议标准&#xff08;是局域网的一种&#xff09;。 以太网是一种…