0%

升级spring-boot至2.6.11

背景

spring-security

为了升级spring-security到5.6.4及以上,spring-security适配的springboot是2.6及以上。

img

spring cloud alibaba

方便以后适配spring cloud alibaba的较新版本。

img\

Spring Cloud

只升级SpringBoot到2.6.11而不升级SpringCloud会有兼容性问题,报错代码参考Caused by: java.lang.ClassNotFoundExceptionimg

PageRequest和Sort

PageRequest和Sort有语法上的变更

image-20240904100610876

kafka

幂等性问题

问题:

升级之后无法正常发送消息

异常信息忘记截图了,跟message version有关

原因:

在kafaka的生产幂等性支持在0.11版本中支持https://www.conduktor.io/kafka/idempotent-kafka-producer/img

目前我们正式的kafka版本为0.10.2.1,测试环境的版本为1.1.0,也就是说正式环境不支持生产幂等性

kafka-clients未升级之前的kafka-clients的版本2.6.0(强制指定),升级之后的kafka-clients版本为3.0https://spring.io/projects/spring-kafka#overviewimg

关于3.0的版本变化可以看KIP-639它将幂等性的配置默认变为true,但是我们正式的kafka还不支持生产幂等性img

解决方式:

下方的兼容建议也是将幂等性配置关闭即可

spring.kafka.producer.properties.enable.idempotence=false

img

Jedis

JedisCommands包路径

image-20240904100723590

未注入IOC的RedisConnectionFactory Bean

image-20240904100906570

之前这里是不用自己调用jedisConnectionFactory.afterPropertiesSet()的,新版本增加了一个成员变量initialized,spring初始化bean的时候将其赋值为true,但是我们这里自己new了一个JedisConnectionFactory的话initialized的默认值为false,如果不调用一次afterPropertiesSet之后会报错:

image-20240904100932360

API

set

image-20240904101012846

RedisScript.of

image-20240904101040150这里也是api的变化引起的。2.1.12.RELEASE中的RedisScript.of方法:

img

2.6.6.RELEASE中的RedisScript.of方法:

img

可以看到方法返回新增了泛型约束,在这里范型为List,List和List不能直接转换,因此报错。

image-20240904101117209

Lettuce

NOAUTH

NOAUTH Authentication required

升级data服务时遇到无法连接redis

img其原因是升级后使用的spring-boot-starter-data-redis版本为2.6.11,data服务未指定RedisConnectionFactory,使用其默认的自动装配类RedisAutoConfiguration完成配置而RedisAutoConfiguration配置类默认使用的redis客户端为Lettuce,使用@Import实现

img在Lettuce6的仓库中确实有关于NOAUTH Authentication required的issues,但是官方并没有提供milestone而是强制指定协议为RESP2来解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.chinaroad.parking.config;

import io.lettuce.core.ClientOptions;
import io.lettuce.core.protocol.ProtocolVersion;
import org.springframework.boot.autoconfigure.data.redis.LettuceClientConfigurationBuilderCustomizer;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;

@Configuration
public class SpringConfig implements LettuceClientConfigurationBuilderCustomizer {

@Override
public void customize(LettuceClientConfiguration.LettuceClientConfigurationBuilder clientConfigurationBuilder) {
// manually specifying RESP2
clientConfigurationBuilder.clientOptions(ClientOptions.builder()
.protocolVersion(ProtocolVersion.RESP2)
.build());
}

}

Mongo

升级SpringBoot到2.6.11以后,mongodb-driver的版本被升到了4.4.2。

image-20240904101247666

mongo兼容性

Mongodb-driver4.x的驱动是兼容3.x的mongo的,这点没问题(目前我们生产环境的mongo大都是3.x的)。img

API

api变化很大,需要修改代码。

MongoDatabaseFactory

image-20240904101409345

image-20240904101434419

isModifiedCountAvailable

变更前

updateResult.isModifiedCountAvailable();

变更后

updateResult.getModifiedCount() > 0

updateResult.isModifiedCountAvailable()这个方法在4.4的mongo-driver中已经没有了,并且从3.12的mongo-driver中就已经标记@Deprecated了,因此这里我直接返回true。img

mongo-java-driver/3.12/javadoc

连接配置修改

imgimg

新版本中需要将mongo连接配置分离,不能全部写入uri中,否则启动会触发校验异常 Invalid mongo configuration, either uri or host/port/credentials/replicaSet must be specifiedimg

修改前

spring.data.mongodb.uri=mongodb://xxx:xxx@mongo.aax6.cn:27017/admin

修改后

1
2
3
4
5
spring.data.mongodb.host=mongo.aax6.cn
spring.data.mongodb.port=27017
spring.data.mongodb.authentication-database=admin
spring.data.mongodb.username=xxx
spring.data.mongodb.password=xxx

_id扁平化处理

官方issues再spring-data-mongo2.0.x中document在反序列化为entity时会经过org.springframework.data.mongodb.core.MongoTemplate.UnwrapAndReadDocumentCallback处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Override
public T doWith(@Nullable Document object) {

if (object == null) {
return null;
}

Object idField = object.get(Fields.UNDERSCORE_ID);

if (!(idField instanceof Document)) {
return super.doWith(object);
}

Document toMap = new Document();
Document nested = (Document) idField;
toMap.putAll(nested);

for (String key : object.keySet()) {
if (!Fields.UNDERSCORE_ID.equals(key)) {
toMap.put(key, object.get(key));
}
}

return super.doWith(toMap);
}

其核心是将_id字段扁平化处理,将字段层级向上提升。但是在spring-data-mongo3.0.x中被取消,导致一些mongo的aggregation查询反序列化失败。解决方法创建监听器监听在反序列化之前发送的事件org.springframework.data.mongodb.core.mapping.event.AfterLoadEvent#AfterLoadEvent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class UnwrapAndReadDocumentListener extends AbstractMongoEventListener<Object>  {

@Override
public void onAfterLoad(AfterLoadEvent<Object> event) {
Document document;
if (event != null && (document = event.getDocument()) != null) {
Object idField = document.get(Fields.UNDERSCORE_ID);
if (idField instanceof Document) {
Document nested = (Document) idField;
document.putAll(nested);
}
}
super.onAfterLoad(event);
}
}

在监听器中再将文档_id扁平化处理即可

count()、countDocuments()、estimatedDocumentCount

3.8驱动的文档描述

img

4.4驱动的文档描述

img

Spring

spring循环依赖

img

2.6开始新增配置,是否允许循环依赖,默认不允许。导致代码中存在大量循环依赖的异常抛出解决方案增加配置

1
spring.main.allow-circular-references=true

Lombok

升级SpringBoot到2.6.11之后, spring-boot-dependencies中约束了lombok的版本为1.18.24,而之前的版本是1.18.12。1.18.24版本的Lombok与mapstruct存在兼容性问题。参考Can I use MapStruct together with Project Lombok?img1.18.16以上的Lombok要想与MapStruct一起使用需要额外引入lombok-mapstruct-binding依赖。

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok-mapstruct-binding -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</dependency>

这里我选择在parent中指定lombok版本。

img

eureka

服务列表

eureka-client与eureka-server版本不一致,导致client无法从server中拉取服务列表

解决方案:

1,升级eureka-extension.jar 为2.0.x 版本

eureka-extension.jar的2.0.x版本中添加配置类RibbonEurekaClientConfig — 自定义bean获取server中的服务列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
* 自定义配置类拉取eureka中的client
*/
public class RibbonEurekaClientConfig {

/**
* 自定义IPing bean
*
* @param config 客户端配置
* @return IPing
*/
@Bean
public IPing ribbonPing(IClientConfig config) {
return new DummyPing();
}

/**
*
* @param config 客户端配置
* @return IRule
* */

@Bean
public IRule ribbonRule(IClientConfig config) {
return new AvailabilityFilteringRule();
}

/**
* 注入发现client
* */
@Autowired
DiscoveryClient discoveryClient;

/**
* 获取服务列表
*
* @param config 客户端配置
* @return serverList<server>
* */
@Bean
public ServerList<Server> getServerList(IClientConfig config) {

return new ServerList<Server>() {
/**
* 初始化server list
* */
@Override
public List<Server> getInitialListOfServers() {
return new ArrayList<>();
}

/**
* 获取更新的server list
* */
@Override
public List<Server> getUpdatedListOfServers() {
List<Server> serverList = new ArrayList<>();

List<ServiceInstance> list = discoveryClient.getInstances(config.getClientName());
for (ServiceInstance instance : list) {
serverList.add(new Server(instance.getHost(), instance.getPort()));
}
return serverList;
}
};
}
}

2,启动类中指定获取服务的bean

1
2
//在启动类上指定获取服务客户端的配置类
@RibbonClients(defaultConfiguration = RibbonEurekaClientConfig.class)

Spring Cloud Config Server

configserver: property to your configuration

官网参考:https://docs.spring.io/spring-cloud-config/docs/current/reference/html/#config-data-import

异常信息

1
2
3
4
5
6
7
8
9
10
Description:

No spring.config.import property has been defined

Action:

Add a spring.config.import=configserver: property to your configuration.
If configuration is not required add spring.config.import=optional:configserver: instead.
To disable this check, set spring.cloud.config.enabled=false or
spring.cloud.config.import-check.enabled=false.

Spring Boot2.4引入了一种通过spring.config.import属性导入配置数据的新方法。这现在是绑定到配置服务器的默认方式。技术上bootstrap.properties和bootstrap.yml 文件已弃用

解决方案1

在文件中添加以下依赖pom.xml项:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

解决方案2:

  1. 将所有属性从boostrap.properties到移动application.properties(也可以.yml)
  2. 删除bootstrap.properties文件
  3. 替换spring.cloud.config.uri=http://localhost:8888为 spring.config.import=configserver:http://localhost:8888

这是告诉您 Spring Boot 应用程序要从运行的 Spring Cloud Config 服务加载属性localhost:8888的正确方法。

swagger

spring-boot2.6.x+与swagger3.0.0-不兼容

注释掉所有swagger相关注解,等下一步推行新的接口文档方案后整体迁移异常提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181)
at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54)
at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356)
at java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155)
at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:745)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:420)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
at so.sao.ucode.data.DataApplication.main(DataApplication.java:44)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: java.lang.NullPointerException: null
at springfox.documentation.spi.service.contexts.Orderings$8.compare(Orderings.java:112)
at springfox.documentation.spi.service.contexts.Orderings$8.compare(Orderings.java:109)
at com.google.common.collect.ComparatorOrdering.compare(ComparatorOrdering.java:38)
at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)
at java.util.TimSort.sort(TimSort.java:234)
at java.util.Arrays.sort(Arrays.java:1438)
at com.google.common.collect.Ordering.sortedCopy(Ordering.java:816)
at springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider.requestHandlers(WebMvcRequestHandlerProvider.java:57)
at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper$2.apply(DocumentationPluginsBootstrapper.java:138)
at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper$2.apply(DocumentationPluginsBootstrapper.java:135)
at com.google.common.collect.Iterators$8.transform(Iterators.java:794)
at com.google.common.collect.TransformedIterator.next(TransformedIterator.java:48)
at com.google.common.collect.TransformedIterator.next(TransformedIterator.java:48)
at com.google.common.collect.Iterators$5.hasNext(Iterators.java:543)
at com.google.common.collect.ImmutableList.copyOf(ImmutableList.java:268)
at com.google.common.collect.ImmutableList.copyOf(ImmutableList.java:226)
at com.google.common.collect.FluentIterable.toList(FluentIterable.java:334)
at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.defaultContextBuilder(DocumentationPluginsBootstrapper.java:111)
at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.buildContext(DocumentationPluginsBootstrapper.java:96)
at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.start(DocumentationPluginsBootstrapper.java:167)
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178)
... 17 common frames omitted

问题分析

Springfox 使用的路径匹配是基于AntPathMatcher的,而Spring Boot 2.6.X使用的是PathPatternMatcher。解决方案一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 增加如下配置可解决Spring Boot 2.6.x 与Swagger 3.0.0 不兼容问题
**/
@Bean
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, Environment environment) {
List<ExposableEndpoint<?>> allEndpoints = new ArrayList();
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
allEndpoints.addAll(webEndpoints);
allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
String basePath = webEndpointProperties.getBasePath();
EndpointMapping endpointMapping = new EndpointMapping(basePath);
boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null);
}
private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {
return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}

maven

maven打包插件版本不兼容问题

dockers启动镜像异常

no main manifest attribute, in app.jar

异常原因: 插件spring-boot-maven-plugin版本过高

解决方案

pom文件中规定maven-plugin版本

1
2
3
4
5
6
7
8
9
10
11
12
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.0.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>

RabbitMQ

Rabbit health check failed

springcloud stream 报错 Rabbit health check failed

原因

就是因为使用了spring.cloud.stream.binders.*.environment属性配置rabbitMQ的相关信息,但是没配置spring.rabbitmq。这就导致自动配置检测到类路径下有rabbit相关的类,就配置了rabbit相关的Bean。其中org.springframework.boot.actuate.amqp.RabbitHealthIndicator负责监控rabbit的连接状况,通过下面这个配置类自动配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package org.springframework.boot.actuate.autoconfigure.amqp;

import java.util.Map;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.actuate.amqp.RabbitHealthIndicator;
import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthContributorConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.health.HealthContributor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RabbitTemplate.class)
@ConditionalOnBean(RabbitTemplate.class)
@ConditionalOnEnabledHealthIndicator("rabbit")
@AutoConfigureAfter(RabbitAutoConfiguration.class)
public class RabbitHealthContributorAutoConfiguration
extends CompositeHealthContributorConfiguration<RabbitHealthIndicator, RabbitTemplate> {

@Bean
@ConditionalOnMissingBean(name = { "rabbitHealthIndicator", "rabbitHealthContributor" })
public HealthContributor rabbitHealthContributor(Map<String, RabbitTemplate> rabbitTemplates) {
return createContributor(rabbitTemplates);
}

}

但是没有检测到spring.rabbitmq相关的配置,就使用了默认的配置,尝试连接localhost:5672,然后就出现了org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connect。问题的关键在于,spring.cloud.stream.binders..environment为每个binder创建了单独的上下文环境,跟application context是完全隔离的。所以,仅仅配置了spring.cloud.stream.binders..environment,使Actuator从application context中没有找到rabbitmq的配置。

解决方案1:(不推荐)

禁用RabbitHealthIndicator。上面提到的配置类上还有一个@ConditionalOnEnabledHealthIndicator(“rabbit”),意思就是:配置management.health.rabbit.enabled为true的时候生效。禁用即可解决。

management.health.rabbit.enable=false

解决方案2:(推荐)

配置spring.rabbitmq用于actuator的健康检测

1
2
3
4
5
6
spring:
rabbitmq:
host: 10.0.40.8
virtual-host: /test
username: guest
password: guest

Junit4迁移指南

https://junit.org/junit5/docs/current/user-guide/#migrating-from-junit4

MapStruct和Lombok

doc: https://mapstruct.org/documentation/stable/reference/html/#lombok推荐配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<properties>
<org.mapstruct.version>1.5.3.Final</org.mapstruct.version>
<org.projectlombok.version>1.18.16</org.projectlombok.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>

<!-- lombok dependency should not end up on classpath -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</path>

<!-- additional annotation processor required as of Lombok 1.18.16 -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>

spring-boot-devtools

异常问题

loader constraint violation: loader (instance of sun/misc/Launcher$AppClassLoader) previously initiated loading for a different type with name “so/sao/ucode/enums/common/Deleted”

原因:

devtools自定义了类加载器,打破了双亲委派机制,导致类加载冲突启动失败devtools的工作原理

spring boot使用了两个类加载器(classloader),对于不会变化的类,比如引入的第三方jar包中的类,加载到一个base classloader中,而开发者实际编写的类,被加载到另一个restart classloader中。当应用重启的时候,整个restart classloader被销毁后重建,而base classloader则保留下来不必再次加载。通过这种方式实现restart效率的提升,模拟了这种方式下的"热部署"。

解决方案

把pom文件中devtools的依赖注释掉。

restTemplate

内部服务调用负载均衡失败

异常问题http://data/test这样的url无法通过restTemplate解析并调用。

原因

可以解析并调用的原理是,将所有添加了@Loadbalanced的RestTemplate添加到拦截器中,在拦截器中解析url,做到一个通过服务名负载均衡到不同的ip。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Configuration(proxyBeanMethods = false)
@Conditional(RetryMissingOrDisabledCondition.class)
static class LoadBalancerInterceptorConfig {

@Bean
public LoadBalancerInterceptor loadBalancerInterceptor(LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}

@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}

}

升级后拦截器的生效条件中新增了,需要配置spring.cloud.loadbalancer.retry.enabled = false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static class RetryMissingOrDisabledCondition extends AnyNestedCondition {

RetryMissingOrDisabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}

@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class RetryTemplateMissing {

}

@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "false")
static class RetryDisabled {

}

}

spring.cloud.loadbalancer.retry.enabled默认为true,也就是默认不支持restTemplate的负载均衡。

解决方案1

增加配置

spring.cloud.loadbalancer.retry.enabled=false

默认重试机制为get请求,重试其他实例1次,因此禁用后影响极其微小。

解决方案二:

改写为feign接口

zip4j 升级

img

img

原setPassword函数没了,密码在构造ZipFile的时候直接传

imgimg

  1. ZipFile 目录变更
  2. 一些配置的变化
  3. 支持 zip 格式规范指定的 unicode 文件名 (UTF-8)。Zip4j 在创建 zip 文件时将使用 UTF-8 文件名和文件注释编码。提取 zip 文件时,Zip4j 将仅使用 UTF-8 编码,前提是根据 zip 文件格式规范设置了适当的标头标志。如果未设置此标志,Zip4j 将使用仅支持扩展 ASCII 字符的 CP437 编码。

spring配置项过期

spring.mvc.locale配置过期

替换为spring.web.locale

解决方案

在core.jar中增加依赖检查配置更新过期, 已防止配置过期产生其他位置问题

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
</dependency>

Jackson

序列化java.time下的类型额外处理

异常

1
Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: java.util.ArrayList[0]->org.springframework.util.LinkedCaseInsensitiveMap["jobExecTime"])

**原因:**com.fasterxml.jackson.databind.util.BeanUtil#checkUnsupportedType

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static String checkUnsupportedType(JavaType type) {
final String className = type.getRawClass().getName();
String typeName, moduleName;

if (isJava8TimeClass(className)) {
// [modules-java8#207]: do NOT check/block helper types in sub-packages,
// but only main-level types (to avoid issues with module)
if (className.indexOf('.', 10) >= 0) {
return null;
}
typeName = "Java 8 date/time";
moduleName = "com.fasterxml.jackson.datatype:jackson-datatype-jsr310";
} else if (isJodaTimeClass(className)) {
typeName = "Joda date/time";
moduleName = "com.fasterxml.jackson.datatype:jackson-datatype-joda";
} else {
return null;
}
return String.format("%s type %s not supported by default: add Module \"%s\" to enable handling",
typeName, ClassUtil.getTypeDescription(type), moduleName);
}

private static boolean isJava8TimeClass(String className) {
return className.startsWith("java.time.");
}

从源码可以看出,jackson无法解析java.time包下的数据类型。LocalDateTime的全路径为java.time.LocalDateTime,被限制序列化。

下图为jdbc版本升级前后(8.0.21–>8.0.30)查出的字段类型比较(左图升级后,右图升级前)

image-20240904102058936

jdbc升级导致字段映射类型变化原因

源码对比

8.0.21com.mysql.cj.jdbc.result.ResultSetImpl#getObject(int)

1
2
case DATETIME:
return this.getTimestamp(columnIndex);

8.0.30com.mysql.cj.jdbc.result.ResultSetImpl#getObject(int)

1
2
case DATETIME:
return getLocalDateTime(columnIndex);

解决方案

引入依赖

1
2
3
4
5
6
<!--解决Jackson时间转换错误-->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<!-- <version>${jackson-datatype-jsr310.version}</version>-->
</dependency>

objectMapper设置配置

objectMapper.registerModule(new JavaTimeModule());

文章作者:xpp011

发布时间:2024年08月22日 - 16:08

原始链接:http://xpp011.cn/2024/08/22/6d00a3a1.html

许可协议: 转载请保留原文链接及作者。