Spring Boot 升級3.x 指南
1. 升級思路
先創建一個parent項目,打包類型為pom,繼承自spring boot的parent項目
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.x</version>
</parent>
然后把版本集中放在這個pom里面,示例如下
<properties><!-- 建議添加全局變量 java.version,maven.compiler.source, maven.compiler.target--><java.version>17</java.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><spring-cloud-dependencies.version>2022.0.4</spring-cloud-dependencies.version><spring-cloud-starter-netflix.version>2.2.10.RELEASE</spring-cloud-starter-netflix.version>
</properties>
然后添加dependencyManagement
節點,示例如下:
<dependencyManagement> <dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud-dependencies.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId><version>${spring-cloud-starter-netflix.version}</version><exclusions><exclusion><artifactId>jsr311-api</artifactId><groupId>javax.ws.rs</groupId></exclusion><exclusion><artifactId>jackson-annotations</artifactId><groupId>com.fasterxml.jackson.core</groupId></exclusion><exclusion><artifactId>jackson-core</artifactId><groupId>com.fasterxml.jackson.core</groupId></exclusion><exclusion><artifactId>jackson-databind</artifactId><groupId>com.fasterxml.jackson.core</groupId></exclusion><exclusion><artifactId>guava</artifactId><groupId>com.google.guava</groupId></exclusion></exclusions></dependency><dependencies>
</dependencyManagement>
其它服務都繼承這個pom文件,這樣各個組件的版本就能統一起來了,將來如果某個組件要升級,直接升級這個項目的版本,其它的重新打包發布即可。
注意:
- 動手前一定要調研項目中使用的組件,某些組件是沒法升級的。比如ElasticSearch,驅動版本和ElasticSearch server版本要一致,升級了就會報錯。其它常見組件的有Nacos,Kafka,Mysql,RocketMQ,需要調研是否兼容低版本。
- 如果之前使用了Zuul 1.x作為Gateway,Zuul 2.x不開源并且難以升級,建議升級到Spring Cloud Gateway
- 如果使用了Zuul1.x的作為Proxy嵌入服務中,有兩個思路,一個是使用Filter+HttpClient手動寫轉發代碼,第二個思路是調研調用接口,使用Feign做轉發
- Spring Boot 3.x 需要JDK17,建議使用OpenJDK17,Oracle的JDK17可能存在授權問題
2. 遇到的問題
-
Zuul1.x 升級Spring Cloud Gateway,可以參考 (Zuul遷移至Spring Cloud Gateway踩坑記錄)[https://blog.csdn.net/codeblf2/article/details/128093298] ,此記錄中服務使用的是k8s部署,轉發直接配置uri,如果使用的是Nacos,網絡上博客比較多,這里不再贅述。
-
如果是Spring Boot 1.x升級上來的,可能要注意循環依賴,添加以下可以解決:
spring:main:allow-circular-references: true
-
Spring Boot 3.x 支持優雅退出,添加以下配置開啟
# 打開優雅退出 server:shutdown: graceful # 多長時間后強制殺掉進程 spring:lifecycle:timeout-per-shutdown-phase: 30s
-
Beancopier
可能沒法用了,可以使用BeanUtil.copyProperties
替換 -
JDK8升級到JDK17,javax包變成了jakarta,需要替換所有的javax.annotation和javax.validation等,但javax.mail沒有變,當jakarta.xxx不存在時,還是使用javax.xxx即可
-
如果引入外部配置文件,使用
spring.cloud.bootstrap.additional-location=/data/config/bootstrap.yml,/data/config/bootstrap2.yml
即可 -
如果依賴的一些jar中依賴一些類但由于升級,依賴類已經不存在了,典型的就是
WebMvcConfigurerAdapter.class
,之前是繼承WebMvcConfigurerAdapter
,Spring Boot 3.x 已經改成了實現接口WebMvcConfigurer
,可能會出現FileNotFoundException
,此時可能難以定位是哪個jar,參考SpringBoot版本升級引起的FileNotFoundException——WebMvcConfigurerAdapter.class -
Spring Cloud Gateway 配置文件參考(可能遇到的問題已經寫在了注釋中):
spring:cloud:gateway:# 默認過濾器default-filters:# 將path中第一個/xxx去掉 比如請求是 https://www.xxx.com/a/b/c?d=1# 經過這個過濾器之后就是 https://www.xxx.com/b/c?d=1- StripPrefix=1# 下面這兩個過濾器是gateway和后面的服務都配置了跨域頭,防止返同樣的回頭有多個導致跨域失敗# 典型的 access-control-allow-credentials: true,true 返回到前端導致跨域失敗- DedupeResponseHeader=access-control-allow-credentials,RETAIN_UNIQUE- DedupeResponseHeader=access-control-allow-origin,RETAIN_UNIQUEroutes:# 服務名- id: user# 轉發到的url 下面的示例是k8s內部轉發# 如果使用服務名轉發 開頭應該是lb:xxx# 這個端口后不要加任何東西 因為轉發的時候會忽略掉# 比如 http://service-user.inner:8080/aaa最后拼接出來是http://service-user.inner:8080,/aaa就忽略了uri: http://service-user.inner:8080predicates:# 匹配的請求url中的path 下面這個會匹配到 http://www.xxx.com/gateway/user/login?userName=AAA- Path=/gateway/user/**filters:# StripPrefix:去除原始請求路徑中的前1級路徑# 會把 http://www.xxx.com/gateway/service1/login中的service1去掉- StripPrefix=1# 在轉發后的url添加的前綴 經過這個filter 轉發url就變成了 http://service-user.inner:8080/service-user- PrefixPath=/service-user# 這里講一下全流程# 以請求為 http://www.xxx.com/gateway/user/login?userName=AAA為例 這個url是要登錄,登錄服務名為service-user# 斷言規則 spring.cloud.routes > predicates > Path=/gateway/user/** 能匹配到url http://www.xxx.com/gateway/user/login?userName=AAA# 第一步是默認過濾器 經過 spring.cloud.gateway.default-filters > StripPrefix=1 這個配置后就變成了 http://www.xxx.com/user/login?userName=AAA# 第二步是routers過濾器 spring.cloud.routes下的id=user的 filters > StripPrefix=1會將 http://www.xxx.com/user/login?userName=AAA的/user去掉,變成了 http://www.xxx.com/login?userName=AAA 變成# 第三步是routers過濾器 spring.cloud.routes下的id=user的 filters > PrefixPath=/service-user 會將 http://www.xxx.com/login?userName=AAA 變成 http://service-user.inner:8080/service-user服務/login?userName=AAA# 經過上面的處理后,最終會轉發到 service-user服務# response中的header如果有跨域header 會經過 spring.cloud.gateway.default-filters > DedupeResponseHeader過濾器將重復的header去掉