麻了!Spring Boot 從 2.2.5 升級到 2.7.2 之后,一堆BUG
往期熱門文章:
1、Spring Boot 實現(xiàn)萬能文件在線預(yù)覽,已開源,真香!!
3、發(fā)現(xiàn)一個Spring事務(wù)的巨坑Bug,可是官方都不承認?大家來評評理!
這篇文章分享一下Spring Boot 升級到2.7的踩坑總結(jié),還是挺全面的,希望對大家有所幫助~
說明
2.7.2為2.x的最后一個穩(wěn)定版本。
3開始最低要求 Java 17,所以暫時不到3.x。
以下的處理方法主要針對我們的項目,可能并不通用。
1、hibernate-validator包下的類報錯
Springboot從2.3以后,spring-boot-starter-web中不再引入hibernate-validator,需要手動引入。
在父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 默認的日志框架是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類中 去掉報錯的方法和屬性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的日期工具類處理,如不滿足再自行擴展
import java.util.Date;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import cn.hutool.core.date.DateUtil;
/**
* @Description 表單形式的全局時間類型轉(zhuǎn)換器
*/
@Configuration
public class DateConverter implements Converter<String, Date> {
// 可以根據(jù)前端傳遞的時間格式自動匹配格式化
@Override
public Date convert(String source) {
return DateUtil.parse(source);
}
}
代碼中不鼓勵依賴循環(huán)引用,默認情況下禁止使用循環(huán)引用。如果能消除bean之間的依賴循環(huán)最好消除,如果實在改動太大,還有一種不推薦的處理方法,設(shè)置 spring.main.allow-circular-references=true
5、swagger錯誤: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默認路徑匹配策略由AntPathMatcher更改為PathPatternParser,因此我們可以通過配置讓其仍使用AntPathMatcher即可。關(guān)注公號:互聯(lián)網(wǎng)架構(gòu)師,回復(fù)關(guān)鍵詞:9,獲取阿里內(nèi)部調(diào)優(yōu)手冊
解決方案:在application.properties里配置:
# 路徑匹配策略使用舊版本的
spring.mvc.pathmatch.matching-strategy= ANT_PATH_MATCHER
順便升級swagger到swagger3,已經(jīng)加到base公共包里了
5.1、修改后路徑需要修改,默認首頁由swagger-ui.html變成了
/swagger-ui/index.html
5.2、如果還想使用擴展的2個ui的版本也需要跟著升級
<swagger-ui-layer.version>1.1.3</swagger-ui-layer.version>
<swagger-bootstrap-ui.version>1.9.6</swagger-bootstrap-ui.version>
我這里直接刪除了那2個ui使用了swagger-bootstrap-ui的升級版: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、刪除項目中自定義的pringfox.documentation.spring.web.readers包
5.5、去掉自定義的頁面,如果想修改找到新的jar包復(fù)制出頁面進行調(diào)整,否則可能看到的頁面里沒有內(nèi)容
src/main/resources/META-INF/resources/doc.html
5.6 調(diào)整過濾器路徑配置
#============================ 安全訪問配置(SecurityFilter)========================
# 需要過濾的urlPatterns,多個用^分隔,沒有或為空則不限制
security.access.urlPatterns = /doc.html^/docs.html^/swagger-ui.html^/swagger-ui/index.html^/v2/api-docs^/swagger-resources
6、跳轉(zhuǎn)登錄頁出錯
如果出現(xiàn)跳轉(zhuǎn)時出錯:
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)換出錯
升級后發(fā)現(xiàn)java中是Date類型,數(shù)據(jù)庫中datetime類型(Timestamp類型沒有問題)的數(shù)據(jù)不是轉(zhuǎn)換為Timestamp,而是直接轉(zhuǎn)為LocalDateTime類型了,解決辦法: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類型的確實換了類型
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對數(shù)據(jù)庫版本有要求,例如flyway-core的當前版本V8.4.3,不能使用 MySQL 5.7, 當flyway-core 降低到V7.15.0后 問題解決,所以匹配flyway-core和數(shù)據(jù)庫版本后問題即可解決。
<properties>
……
<flyway.version>7.15.0</flyway.version>
</properties>
……
<!-- 添加 flyway 的依賴,flyway需要區(qū)分版本,不同版本對不同數(shù)據(jù)庫版本支持不同 -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>${flyway.version}</version>
</dependency>
……
9、Junit運行后沒有反應(yīng)
升級后默認使用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
//使用時
@RunWith(SpringRunner.class)》@ExtendWith(SpringExtension.class)
10、升級后json中Long類型字段精度丟失
344280995828072448》344280995828072450
344268472663932928》344268472663932900
343301120241696768》343301120241696800
問題原因:經(jīng)查看,默認已經(jīng)有多個消息轉(zhuǎn)換器了。而 configureMessageConverters 方法中是一個 list 參數(shù)。直接向其中添加 HttpMessageConverter 后,默認是排在最后的。就造成了你自定義的消息轉(zhuǎn)換器不生效。其實是被其他轉(zhuǎn)換器接管了。
解決辦法:加到第一個就行了。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);
}
往期熱門文章:
1、JDK 19 / Java 19 正式發(fā)布,虛擬線程來了!
2、手擼一個讀寫分離中間件
3、Spring Boot官宣:正式棄用 Java 8 啦
4、MySQL 上億大表如何優(yōu)化?
5、我找到了一個快速定位Spring Boot接口超時問題的神器!
6、很不起眼的6個bug,90%的程序員就算寫了10年代碼也肯定都踩過!
7、一個程序員的水平能差到什么程度?
8、MyBatis批量插入幾千條數(shù)據(jù)慎用foreach
9、Spring Boot加一個注解,輕松實現(xiàn) Redis 分布式鎖
10、面試官問:select......for update會鎖表還是鎖行?
