麻了!Spring Boot 從 2.2.5 升級(jí)到 2.7.2 之后,一堆BUG

這篇文章分享一下Spring Boot 升級(jí)到2.7的踩坑總結(jié),還是挺全面的,希望對(duì)大家有所幫助~
說明
2.7.2為2.x的最后一個(gè)穩(wěn)定版本。
3開始最低要求 Java 17,所以暫時(shí)不到3.x。
以下的處理方法主要針對(duì)我們的項(xiàng)目,可能并不通用。
1、hibernate-validator包下的類報(bào)錯(cuò)
Springboot從2.3以后,spring-boot-starter-web中不再引入hibernate-validator,需要手動(dòng)引入。
在父pom中引入,已經(jīng)加入software-center-modules模塊中,子模塊不需要加:
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.18.Final</version>
<scope>compile</scope>
</dependency>
2、ErrorController無getErrorPath方法
去掉該方法
3、logback和log4j沖突
org.apache.logging.log4j.LoggingException: log4j-slf4j-impl cannot be present with log4j-to-slf4j
at org.apache.logging.slf4j.Log4jLoggerFactory.validateContext(Log4jLoggerFactory.java:60)
at org.apache.logging.slf4j.Log4jLoggerFactory.newLogger(Log4jLoggerFactory.java:44)
at org.apache.logging.slf4j.Log4jLoggerFactory.newLogger(Log4jLoggerFactory.java:33)
at org.apache.logging.log4j.spi.AbstractLoggerAdapter.getLogger(AbstractLoggerAdapter.java:53)
at org.apache.logging.slf4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:33)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:363)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:388)
at com.ld.CreditTaskManageApplication.<clinit>(CreditTaskManageApplication.java:40)
... 34 more
排除掉springboot的logging
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
<exclusions>
<!-- spring boot 默認(rèn)的日志框架是Logback,所以在引用log4j之前,需要先排除該包的依賴,再引入log4j2的依賴 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
4、循環(huán)依賴:The dependencies of some of the beans in the application context form a cycle
[credit-task-manage]2022-08-04 13:54:43.411 [WARN]:Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'webMainConfig': Unsatisfied dependency expressed through field 'handlerAdapter'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration': Unsatisfied dependency expressed through method 'setConfigurers' parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'webMainConfig': Requested bean is currently in creation: Is there an unresolvable circular reference? org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:591)
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| webMainConfig (field private org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter com.ld.common.config.WebMainConfig.handlerAdapter)
↑ ↓
| org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration
└─────┘
Action:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter.report(LoggingFailureAnalysisReporter.java:40)
WebMainConfig類中 去掉報(bào)錯(cuò)的方法和屬性handlerAdapter,修改寫法
@Bean
public ConversionService getConversionService(DateConverter dateConverter) {
ConversionServiceFactoryBean factoryBean = new ConversionServiceFactoryBean();
Set<Converter<String, Date>> converters = new HashSet<>();
converters.add(dateConverter);
factoryBean.setConverters(converters);
return factoryBean.getObject();
}
日期轉(zhuǎn)換器,依賴hutool的日期工具類處理,如不滿足再自行擴(kuò)展
import java.util.Date;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import cn.hutool.core.date.DateUtil;
/**
* @Description 表單形式的全局時(shí)間類型轉(zhuǎn)換器
*/
@Configuration
public class DateConverter implements Converter<String, Date> {
// 可以根據(jù)前端傳遞的時(shí)間格式自動(dòng)匹配格式化
@Override
public Date convert(String source) {
return DateUtil.parse(source);
}
}
代碼中不鼓勵(lì)依賴循環(huán)引用,默認(rèn)情況下禁止使用循環(huán)引用。如果能消除bean之間的依賴循環(huán)最好消除,如果實(shí)在改動(dòng)太大,還有一種不推薦的處理方法,設(shè)置 spring.main.allow-circular-references=true
5、swagger錯(cuò)誤:Failed to start bean 'documentationPluginsBootstrapper'
Application run failed org.springframework.boot.SpringApplication.reportFailure(SpringApplication.java:824)
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) ~[spring-context-5.3.22.jar:5.3.22]
查資料發(fā)現(xiàn)是新版本Spring Boot將Spring MVC默認(rèn)路徑匹配策略由AntPathMatcher更改為PathPatternParser,因此我們可以通過配置讓其仍使用AntPathMatcher即可。關(guān)注公號(hào):互聯(lián)網(wǎng)架構(gòu)師,回復(fù)關(guān)鍵詞:9,獲取阿里內(nèi)部調(diào)優(yōu)手冊(cè)
解決方案:在application.properties里配置:
# 路徑匹配策略使用舊版本的
spring.mvc.pathmatch.matching-strategy= ANT_PATH_MATCHER
順便升級(jí)swagger到swagger3,已經(jīng)加到base公共包里了
5.1、修改后路徑需要修改,默認(rèn)首頁由swagger-ui.html變成了
/swagger-ui/index.html
5.2、如果還想使用擴(kuò)展的2個(gè)ui的版本也需要跟著升級(jí)
<swagger-ui-layer.version>1.1.3</swagger-ui-layer.version>
<swagger-bootstrap-ui.version>1.9.6</swagger-bootstrap-ui.version>
我這里直接刪除了那2個(gè)ui使用了swagger-bootstrap-ui的升級(jí)版:knife4j。base模塊中已經(jīng)引入
<knife4j.version>3.0.3</knife4j.version>
……
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
5.3、swagger的配置類,注解@EnableSwagger2去掉,名字改為更通用的SwaggerConfig
@Configuration
//@EnableSwagger2
@Slf4j
public class SwaggerConfig {
}
5.4、刪除項(xiàng)目中自定義的pringfox.documentation.spring.web.readers包
5.5、去掉自定義的頁面,如果想修改找到新的jar包復(fù)制出頁面進(jìn)行調(diào)整,否則可能看到的頁面里沒有內(nèi)容
src/main/resources/META-INF/resources/doc.html
5.6 調(diào)整過濾器路徑配置
#============================ 安全訪問配置(SecurityFilter)========================
# 需要過濾的urlPatterns,多個(gè)用^分隔,沒有或?yàn)榭談t不限制
security.access.urlPatterns = /doc.html^/docs.html^/swagger-ui.html^/swagger-ui/index.html^/v2/api-docs^/swagger-resources
6、跳轉(zhuǎn)登錄頁出錯(cuò)
如果出現(xiàn)跳轉(zhuǎn)時(shí)出錯(cuò):
Cannot forward to error page for request [/a/] as the response has already been committed. As a result, the response may have the wrong status code. If your application is running on WebSphere Application Server you may be able to resolve this problem by setting com.ibm.ws.webcontainer.invokeFlushAfterService to false org.springframework.boot.web.servlet.support.ErrorPageFilter.handleCommittedResponse(ErrorPageFilter.java:219)
解決方案同5
7、日期轉(zhuǎn)換出錯(cuò)
升級(jí)后發(fā)現(xiàn)java中是Date類型,數(shù)據(jù)庫中datetime類型(Timestamp類型沒有問題)的數(shù)據(jù)不是轉(zhuǎn)換為Timestamp,而是直接轉(zhuǎn)為L(zhǎng)ocalDateTime類型了,解決辦法:com.ld.shieldsb.dao.MyBeanProcessor修改type2Bean方法,增加LocalDateTime和LocalDate的處理
if (value != null && fieldType.equals(Date.class)) {
if (value.getClass().equals(String.class)) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
field.set(model, sdf.parse((String) value));
} catch (ParseException e) {
log.error("日期字段讀取失敗," + fieldType + ",error:" + e.getMessage());
}
} else if (value.getClass().equals(LocalDateTime.class)) {
field.set(model, DateTimeUtil.localDateTime2Date((LocalDateTime) value));
} else if (value.getClass().equals(LocalDate.class)) {
field.set(model, DateTimeUtil.localDate2Date((LocalDate) value));
} else {
field.set(model, value);
}
}
我們使用的是mysql查看依賴jar包看到mysql-connector-java的版本從8.0.19變成了8.0.29
原因找到com.mysql.cj.jdbc.result.ResultSetImpl類的getObject(int columnIndex)方法可以看到Datetime類型的確實(shí)換了類型
case DATETIME:
return getTimestamp(columnIndex);
// 改為了
case DATETIME:
return getLocalDateTime(columnIndex);
8、flyway:org.flywaydb.core.api.FlywayException: Unsupported Database: MySQL 5.7
Application run failed org.springframework.boot.SpringApplication.reportFailure(SpringApplication.java:824)
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flywayInitializer' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Invocation of init method failed; nested exception is org.flywaydb.core.api.FlywayException: Unsupported Database: MySQL 5.7
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804) ~[spring-beans-5.3.22.jar:5.3.22]
……
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:343) [bootstrap.jar:9.0.31]
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:474) [bootstrap.jar:9.0.31]
Caused by: org.flywaydb.core.api.FlywayException: Unsupported Database: MySQL 5.7
at org.flywaydb.core.internal.database.DatabaseTypeRegister.getDatabaseTypeForConnection(DatabaseTypeRegister.java:106) ~[flyway-core-8.5.13.jar:?]
……org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800) ~[spring-beans-5.3.22.jar:5.3.22]
... 49 more
flyway對(duì)數(shù)據(jù)庫版本有要求,例如flyway-core的當(dāng)前版本V8.4.3,不能使用 MySQL 5.7, 當(dāng)flyway-core 降低到V7.15.0后 問題解決,所以匹配flyway-core和數(shù)據(jù)庫版本后問題即可解決。
<properties>
……
<flyway.version>7.15.0</flyway.version>
</properties>
……
<!-- 添加 flyway 的依賴,flyway需要區(qū)分版本,不同版本對(duì)不同數(shù)據(jù)庫版本支持不同 -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>${flyway.version}</version>
</dependency>
……
9、Junit運(yùn)行后沒有反應(yīng)
升級(jí)后默認(rèn)使用junit5,而依賴的jar包中引入了junit4的jar包沖突了,去掉junit4的jar包即可。
注意使用junit5后包的名字發(fā)生了變化,下面箭頭前后分別是junit4和junit5的
org.junit.Test》org.junit.jupiter.api.Test
org.junit.runner.RunWith》org.junit.jupiter.api.extension.ExtendWith
//使用時(shí)
@RunWith(SpringRunner.class)》@ExtendWith(SpringExtension.class)
10、升級(jí)后json中Long類型字段精度丟失
344280995828072448》344280995828072450
344268472663932928》344268472663932900
343301120241696768》343301120241696800
問題原因:經(jīng)查看,默認(rèn)已經(jīng)有多個(gè)消息轉(zhuǎn)換器了。而 configureMessageConverters 方法中是一個(gè) list 參數(shù)。直接向其中添加 HttpMessageConverter 后,默認(rèn)是排在最后的。就造成了你自定義的消息轉(zhuǎn)換器不生效。其實(shí)是被其他轉(zhuǎn)換器接管了。
解決辦法:加到第一個(gè)就行了。add(0, customConverter())
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
……
// 支持text 轉(zhuǎn)string
converters.add(0, customJackson2HttpMessageConverter());
converters.add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
converters.add(0, fastConverter);
}
如果使用的是@bean注解,覆蓋的fastjson則不需要改,如下:
@Bean
public HttpMessageConverters customConverters() {
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
// 創(chuàng)建配置類
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteNullStringAsEmpty);
// 解決 Long 轉(zhuǎn)json 精度丟失的問題
SerializeConfig serializeConfig = SerializeConfig.globalInstance;
serializeConfig.put(BigInteger.class, ToStringSerializer.instance);
serializeConfig.put(Long.class, ToStringSerializer.instance);
serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
fastJsonConfig.setSerializeConfig(serializeConfig);
// 此處是全局處理方式
fastJsonConfig.setDateFormat(DATE_FORMAT);
fastJsonConfig.setCharset(StandardCharsets.UTF_8);
fastConverter.setFastJsonConfig(fastJsonConfig);
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
fastConverter.setSupportedMediaTypes(supportedMediaTypes);
// 支持text 轉(zhuǎn)string
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
return new HttpMessageConverters(fastConverter, stringHttpMessageConverter);
}------------- END ------------- 掃描下方二維碼,加入技術(shù)群。暗號(hào):加群
