SpringBoot 踩坑合集| 青训营笔记

750 阅读9分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第5篇笔记。

H2 Database 2.0+ 相关的SQL语法问题

Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement ""; expected "identifier";

错误如上,在学习一个SpringBoot教程介绍了了H2 Database,教程是SpringBoot 2.5.3,到发帖时最新的稳定版是SpringBoot 2.7.0,但是新版SpringBoot会产生一个问题,即Hibernate无法为H2数据库生成合法的SQL语句,目前最简单的解决方法是降级H2依赖版本至1.+。示例如下:

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.200</version>
</dependency>

目前不清楚具体的原因到底什么,可能是违反了Not NULL相关的约束条件。

参考:

HHH-14985 H2Dialect does not work properly with h2 2.0.202 on inserts

Upgrade H2 version 2.0.204 from 1.4.200

H2 version change issue from 1.4.200 to 2.0.202?

高版本SpringBoot配置Swagger2的问题

这个问题来自mall-learning项目。 这一部分主要想解决的问题是更新SpringBoot版本至2.6.4+之后会导致Swagger2报空指针异常导致启动失败。 我也按作者的思路补充了一个SpringBoot 2.7.0的最小Swagger配置示例mall-tiny-swagger2

为了解决报错问题,首先需要增加一个Bean Swagger2Config.java,因为我对Spring还不是很熟悉,从功能上看应该是在Spring Bean注册之后 修改Bean的一些属性,使得Spring框架可以发现和注册Swagger相关的Bean。代码如下:

@Bean
public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
    return new BeanPostProcessor() {
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
                customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
            }
            return bean;
        }
        
        private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
            List<T> copy = mappings.stream().filter(mapping -> mapping.getPatternParser() == null).collect(Collectors.toList());
            mappings.clear();
            mappings.addAll(copy);
        }
        
        private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
            try {
                Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
                field.setAccessible(true);
                return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                throw new IllegalStateException(e);
            }
        }
    };
}

其次因为文档无法正常显示,需要修改Spring的配置,即修改application.yml,其中server端口是8088,作者增加了mvc.pathmatch.matching-strategy.ANT_PATH_MATCHER来修正文档无法正常显示,虽然不清楚这个选项的原理和作用。 如果在本机ip启动项目,在查看Swagger时需要到http://localhost:8088/swagger-ui/ 而不是http://localhost:8088/swagger-ui.html ,似乎新版本的Swagger位置不同了?

项目原作者的博客:升级 SpringBoot 2.6.x 版本后,Swagger 没法用了。。。

如果碰到mall项目缺少ums_admin等表的错误,去项目作者仓库中的document文件夹下载mall.sql文件导入到数据库中。

如果碰到Swagger token使用失败,可能是数据库数据设置的有一些小问题,可以考虑改用作者专门为tiny项目准备的sql文件mall_tiny.sql

java.lang.ClassNotFoundException: javax.xml.bind.DatatypeConverter

解决方案:

  1. 降低JDK版本至至少JDK 8,在IDEA中,需要将项目的JDK降低至JDK8而不仅仅是修改pom等依赖配置文件。

  2. 以maven为例,添加javax.xml.bind依赖,目前最新版本为2.3.1

<dependency> 
    <groupId>javax.xml.bind</groupId> 
    <artifactId>jaxb-api</artifactId> 
</dependency>

原因: The JAXB APIs are considered to be Java EE APIs and therefore are no longer contained on the default classpath in Java SE 9. In Java 11, they are completely removed from the JDK. Fortunately, these Java EE APIs that were provided in JDK 6/7/8 are still in the JDK, but they just aren't on the classpath by default.

翻译:JAXB APIs被视为JavaEE APIs,因此不再包含在JavaSE 9的默认类路径中。在Java 11中已经从JDK中移除。这些JavaEE APIs仍然在JDK 6/7/8中提供,但是不再默认包含在类路径中。

参考: How to resolve java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

真正解决方案:java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter

@NotEmpty注解

SpringBoot 2.3.x开始不会集成Hibernate的一些验证约束类Hibernate @NotEmpty is deprecated,转而使用javax.validation.constrains的约束类,如果仍然想使用Hibernate可以手动添加如下依赖,如果想改用新的可以import javax.validation.constraints.NotEmpty;。试了一下,SpringBoot从2.3.x开始不会自动集成这个依赖了。

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>7.0.4.Final</version>
</dependency>

Nacos Config配置无法加载的问题

引流之术: boostrap.yml失效/读取不到nacos中的配置/获取不到配置内容的问题/可以注册服务但是无法获取配置文件

spring cloud 2020.x.x版本会出现因为不读取bootstrap.yml或bootstrap.properties文件失效导致无法获取配置的问题,新版spring cloud默认不读取bootstrap配置文件,需要增加下列依赖。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

Spring bootstrap.yml 不管用,失效解决方案(spring cloud 2020.x.x)

SpringBoot ElasticSearch @Document注解缺少type以及shards和replicas弃用

根据官方文档Documentation,Document的注解有一部分移到Setting注解, 那么猜测下列用法是等同的

// 低版本
@Document(indexName = "", shards = 1, replicas = 0)
// 高版本
@Document(indexName = "")
@Setting(shards = 1, replicas = 0)

跨域配置

CorsConfiguration config = new CorsConfiguration();
// config.addAllowedOrigin("*); 这种方式会出一些问题
config.addAllowedOriginPattern("*");

根据项目作者的说法,可能type就完全没有了,不过我不清楚这个type的作用,自定义标识类型吗?spring-data-elasticsearch entity removing deprecated type

这里似乎还有另一个问题,在抄尚硅谷的谷粒商城的时候,当时可以用的org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource.addAllowedOrigin 已经不能用了,需要改成addAllowedOriginPattern,而且在后台项目登录时碰到了下面这个问题,注释掉这个配置又出现未配置错误。目前没搞明白为什么配置里的Bean会重复调用2次。 破案了,内层微服务也有跨域配置。因为网关不配置时,内层微服务也不会响应,所以是未配置,网关配置时又会配置2次。注释掉内层微服务跨域配置。

The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:8001, http://localhost:8001', but only one is allowed.

所以按StackOverflow一个帖子在配置里做了限制'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed

 gateway:
  default-filters:
    - DedupeResponseHeader=Access-Control-Allow-Origin

SpringBoot配置ElasticSearch相关问题

ElasticSearch Docker容器配置环境

SpringBoot至少到2.3.x以上才能兼容7以上的ElasticSearch,以下以Windows系统为例说明,默认已安装好Docker-desktop。

Elastic Search 7.x

  1. 安装ElasticSearch
# 拉取ElasticSearch镜像
docker pull elasticsearch:7.17.3
# -d 后台运行, --name 配置容器名称, -p 配置端口映射, -e 设置容器环境变量
# 这里设置为单节点以避免Elastic Search 7.x版本避免下列问题,由于分词器要求版本匹配所以安装7.17.3版本
# [discovery.seed_hosts, discovery.seed_providers, cluster.initial_master_nodes]
docker run -d --name es -p 9200:9200 -p 9300:9300  -e "discovery.type=single-node" elasticsearch:7.17.3
# 拉取Kibana镜像,Kibana属于ElasticSearch的配套工具,可以不安装也不启动
docker pull kibana:7.17.3
# 这里需要指明Elastic Search地址,注意localhost应为宿主机地址,否则需要将两个容器配置到同一个docker网络中,然后将localhost改为容器名作为域名
docker run -d --name kibana -p 5601:5601 -e "ELASTICSEARCH_HOSTS=http://localhost:9200" kibana:7.17.3
  1. 安装分词器 运行SpringBoot应用时可能会遇到下列问题,一种可能的原因是没有安装合适的分词器。
Elasticsearch exception [type=illegal_argument_exception, reason=analyzer [] has not been configured in mappings]

使用下列命令可以安装分词器,可以去这个开源分词器的仓库寻找特定的版本,这个分词器要求版本与ElasticSearch一致,比如说ElasticSearch 7.17.3就只能装对应的7.17.3版本。安装成功后注意重新启动容器。

# 进入ElasticSearch容器
docker exec -it es /bin/bash
# 安装分词器
root@595a300c48d9:/usr/share/elasticsearch# bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.3/elasticsearch-analysis-ik-7.17.3.zip

参考的博客有些会建议修改配置以满足跨域请求,不太清楚这个配置影响哪些场景

# 进入ElasticSearch容器
docker exec -it es /bin/bash
# cd /usr/share/elasticsearch
vi config/elasticsearch.yml

在配置文件中增加下列选项:

http.cors.enabled: true
http.cors.allow-origin: "*"

访问http://localhost:9200/查看响应以确定是否已经配置并启动好。

Elastic Search 8.x

  1. 安装ElasticSearch,ElasticSearch 8.x开始默认启用https和安全策略,所以注意记录首次安装时输出的默认安全信息,如密码、token等等。
# 拉取ElasticSearch镜像
docker pull elasticsearch:8.2.2
# 注意,这里采用-it参数安装,避免输出丢失,在输出中需要找到后附形式的一些内容用来配置访问
docker run -it --name es -p 9200:9200 -p 9300:9300  -e "discovery.type=single-node" elasticsearch:8.2.2
# 拉取Kibana镜像
docker pull kibana:8.2.2
# 这里需要指明Elastic Search地址
docker run -d --name kibana -p 5601:5601 kibana:8.2.2

命令行输出中的安全信息示例:

-> Elasticsearch security features have been automatically configured!
-> Authentication is enabled and cluster connections are encrypted.
->  Password for the elastic user (reset with `bin/elasticsearch-reset-password -u elastic`):
  VUasYgQSz3mq6QgzPoFD
->  HTTP CA certificate SHA-256 fingerprint:
  61c5a75171432b354bc5ca4a3c5c4f301a96472516b359a866bdefb92157a9d5
->  Configure Kibana to use this cluster:
* Run Kibana and click the configuration link in the terminal when Kibana starts.
* Copy the following enrollment token and paste it into Kibana in your browser (valid for the next 30 minutes): eyJ2ZXIiOiI4LjIuMiIsImFkciI6WyIxNzIuMTcuMC4yOjkyMDAiXSwiZmdyIjoiNjFjNWE3NTE3MTQzMmIzNTRiYzVjYTRhM2M1YzRmMzAxYTk2NDcyNTE2YjM1OWE4NjZiZGVmYjkyMTU3YTlkNSIsImtleSI6ImFacDVLSUVCa09TY2twZHJpbEptOlV5d2ZrZVZnUk1hanlhZDFQZHVFclEifQ==
-> Configure other nodes to join this cluster:
* Copy the following enrollment token and start new Elasticsearch nodes with `bin/elasticsearch --enrollment-token <token>` (valid for the next 30 minutes): eyJ2ZXIiOiI4LjIuMiIsImFkciI6WyIxNzIuMTcuMC4yOjkyMDAiXSwiZmdyIjoiNjFjNWE3NTE3MTQzMmIzNTRiYzVjYTRhM2M1YzRmMzAxYTk2NDcyNTE2YjM1OWE4NjZiZGVmYjkyMTU3YTlkNSIsImtleSI6ImFKcDVLSUVCa09TY2twZHJpbEptOktiN1kyOUNxUjNlc2Vic2pVeDg3THcifQ==
  If you're running in Docker, copy the enrollment token and run:
  `docker run -e "ENROLLMENT_TOKEN=<token>" docker.elastic.co/elasticsearch/elasticsearch:8.2.2`

访问https://localhost:9200/查看响应以确定是否已经配置并启动好。注意,这里是https而不是http,需要输入用户名elastic和对应的密码,见上文的安全信息部分。

ElasticSearch localhost:9200无响应方案

从ElasticSearch 7开始似乎就逐步启用9300端口而改用9200端口。 这篇写的很详细了Elastic:使用 Docker 安装 Elastic Stack 8.0 并开始使用

ElasticSearch Docker运行启动失败之max virtual memory areas vm.max_map_count 65530 is too low

这个问题似乎只出现在单节点(single-node)配置下,对于Windows系统可以使用下列命令进入docker的WSL中修改系统配置,重启系统后不会失效,而另一种会。

wsl -d docker-desktop
echo 262144 >> /proc/sys/vm/max_map_count

Elastic Docker安装教程推荐

ElasticSearch之环境搭建

Docker下安装ElasticSearch和Kibana

Ambiguous collection type for property 'salesDetailList'. You must specify 'javaType' or 'resultMap'

在MyBatis的XML配置文件中,找到对应的ResultMap,在collection标签内增加javaType="java.util.List",如下所示。

<resultMap id="" type="" autoMapping="true">
    <id column="id" jdbcType="BIGINT" property="id" />
    <collection property="attrValueList" columnPrefix="attr_" ofType="" javaType="java.util.List">
        <id column="id" property="id" jdbcType="BIGINT"/>
    </collection>
</resultMap>

RabbitMQ Docker安装问题

注意拉取rabbitmq Docker Offcial Imagex.x.x-management镜像,这样才能进入各大教程中说的localhost:15672的管理页面。