记一次框架升级

news2024/9/24 19:15:09

     背景

        随着公司业务的不断扩展,新技术的更新换代,企业内部免不了会对软硬件进行升级,淘汰老旧的组件和实现方案,更新一波技术栈。这不,最近我们公司就面临这么一个难题:旧版本的组件上发现漏洞,为了修复系统漏洞,不得不对它进行版本升级。而升级某个组件时,又会因为版本不兼容,配置更新等原因,必须同步调整其他的组件或配置。因此,看似简单的升级一个组件,却像是推倒了多米诺骨牌似的,越改越多,越理越乱。而受影响的项目又多,每个项目都得有技术人员花费大量时间去处理这种廉价而繁琐的事情,事倍而功半。公司的CTO知道这事后,觉得长痛不如短痛,被动防守不如主动出击,咱干脆借着处理这件事,来一次架构升级,把SpringBoot改成SpringBoot3,JVM从1.8升到17,把公司该升的组件都升了。

        而不才,就悲催荣幸的被委予此项重任。

      框架升级

      一、需求调研

        框架是为了业务需求而服务的,一个好的框架能为整个研发团队降本增效,用更便利,更优雅的方式实现需求。 因此,在升级框架之前,必须得进行一轮需求调研。我联系了公司几个产品线的Leader,通过收集当前产品线所用框架中包含的组件,以及公司的业务诉求,推导出了新框架的建设清单:

新框架技术栈版本类别说明当前现状
JDK17后端Java开发环境JDK 1.8
Spring Boot3.1.9后端后端研发框架Spring Boot 2.1.6.RELEASE
Spring Cloud Alibaba2022.0.0.0后端微服务框架Spring Cloud 2.1.2.RELEASE
Nacos 2.3.2中间件配置中心 & 注册中心Apollo 1.1.0+Eureka2.1.2.RELEASE
RocketMQ5.1.0中间件消息队列不涉及
Sentinel1.8.6开源基础组件服务保障/容错不涉及
XXL-JOB2.4.0中间件定时任务XXL-JOB 2.4.1-SNAPSHOT
Spring Cloud Gateway4.0.6中间件服务网关3.0.6
seata1.6.1中间件分布式事务不涉及
DMV8数据库数据库服务器MYSQL
Druid1.2.22基础组件JDBC 连接池、监控组件Druid 1.1.10
Dynamic DataSource4.3.0基础组件动态数据源,可以实现数据源动态切换不涉及
Redis6.2.7数据库key-value 数据库6.2.7
redisson3.24.3工具Redis 客户端redisson 3.11.2
hibernate-validator8.0.1基础组件参数校验组件hibernate-validator 6.0.17 Final
flowable7.0.0基础组件工作流引擎flowable 6.8.0
knife4j4.4.0基础组件Swagger 增强 UI 实现Swagger1.7.0
Apache SkyWalking 9.7.0中间件分布式应用追踪系统不涉及
Plumelog3.5中间件统一日志平台Logstash 5.3
fastjson22.0.50基础组件JSON 工具库fastjson2 1.2.13
MapStruct1.5.5.Final基础组件Java Bean 转换MapStruct 1.5.2.Final
Project Lombok1.18.30基础组件消除冗长的 Java 代码Project Lombok 1.18.8
JUnit4.13.2基础组件Java 单元测试框架 4.12
mockito5.7.0基础组件Java Mock 框架不涉及
Mybatis-Plus3.5.5基础组件数据库操作组件Mybatis-Plus 3.4.6,tk.mybatis 2.1.5
Nginx1.24中间件反向代理,负载均衡,请求转发Nginx 1.13.7
poi5.2.5基础组件文档在线处理组件poi 4.1.0
easyexcel3.3.4基础组件excel在线处理组件easyexcel 2.1.6
codec1.17.0基础组件数据编码解码组件不涉及
hutool5.8.27基础组件开源工具包hutool 5.3.0
commons-net3.11.1基础组件Ftp连接组件不涉及
beanutils1.9.4基础组件Java基础类库beanutils 1.9.3
aviator5.4.1基础组件表达式处理组件不涉及
jjwt0.12.5基础组件数字签名组件jwt 0.9.0
OAuth26.3.1中间件认证服务OAuth2 2.3.3.RELEASE
minio8.5.10基础组件对象存储服务器与新框架一致
pagehelper2.1.0基础组件分页组件与新框架一致

        在规划这些组件/依赖的版本时,我直接根据名字去https://mvnrepository.com/中查询当前组件的最高版本,毕竟JDK,Springboot都是用的比较新的版本,完全Hold住……于是后面就踩坑了,至于是什么坑,请看下章分解。

     二、父工程搭建

      有了上面的清单,就可以搭建父工程了,父工程其实很简单,一个pom.xml足矣。而pom.xml里的内容,就是上面的清单的具象化。如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <artifactId>leixi-hub-parent</artifactId>
  <packaging>pom</packaging>
  <name>leixi-hub-parent</name>
  <description>leixi-hub 父工程</description>
  <groupId>com.leixi.hub</groupId>
  <version>${leixi-hub.version}</version>

  <properties>
    <!--  按字母顺序  -->
    <apm-toolkit-trace.version>6.5.0</apm-toolkit-trace.version>
    <leixi-hub.version>1.0.0-SNAPSHOT</leixi-hub.version>
    <commons-beanutils.version>1.9.4</commons-beanutils.version>
    <commons-io.version>2.15.0</commons-io.version>
    <commons-lang.version>2.6</commons-lang.version>
    <commons-codec.version>1.17.0</commons-codec.version>
    <dm8.version>8.1.1.193</dm8.version>
    <druid.version>1.2.22</druid.version>
    <discovery.version>6.21.0</discovery.version>
    <dynamic.datasource.version>4.3.0</dynamic.datasource.version>
    <ehcache.version>2.10.6</ehcache.version>
    <fastjson.version>2.0.50</fastjson.version>
    <grpc-spring-boot.version>3.0.0.RELEASE</grpc-spring-boot.version>
    <grpc-bom.version>1.60.1</grpc-bom.version>   <!--用于构建分布式系统中的服务和客户端-->
    <protobuf-bom.version>3.25.2</protobuf-bom.version>   <!--是一个 Maven BOM(Bill of Materials)文件,用于管理 Protocol Buffers(protobuf),-->
    <hutool.version>5.8.27</hutool.version>
    <kaptcha.version>2.3.2</kaptcha.version>
    <java.version>17</java.version>
    <jdom.version>1.1</jdom.version>   <!--操作xml的组件,考虑去掉-->
    <jakarta-persistence-api.version>3.1.0</jakarta-persistence-api.version>   <!--一个 Java 规范,用于定义和管理对象关系映射?-->
    <jackson.version>2.16.1</jackson.version>   <!--用于提供对 JSR-310(Java 日期和时间 API)的支持-->
    <junit.version>4.13.2</junit.version>
    <jetcache.version>2.7.5</jetcache.version>
    <knife4j.version>4.4.0</knife4j.version>
    <lombok.version>1.18.30</lombok.version>
    <lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
    <micrometer.version>1.12.2</micrometer.version> <!--  micrometer-core 是一个用于度量和监控应用程序的 Java 库。收集和报告与应用程序性能相关的指标和度量-->
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <mybatis-spring.version>3.0.2</mybatis-spring.version>
    <mybatis-plus.version>3.5.5</mybatis-plus.version>
    <mapstruct.version>1.5.5.Final</mapstruct.version>
    <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <poi.version>5.2.5</poi.version>
    <page-helper.version>2.1.0</page-helper.version>
    <plumelog.version>3.5.3</plumelog.version>
    <pinyin4j.version>2.5.0</pinyin4j.version>  <!--用于将汉字转换为拼音-->
    <!--  消息队列  -->
    <rocketmq-spring.version>2.2.3</rocketmq-spring.version>
    <rocketmq.version>5.1.0</rocketmq.version>
    <rocketmq.spring.client.version>5.0.5</rocketmq.spring.client.version>
    <redisson.version>3.24.3</redisson.version>
    <spring-boot.version>3.1.9</spring-boot.version>
    <spring-cloud.version>2022.0.3</spring-cloud.version>
    <spring.cloud.alibaba.version>2022.0.0.0</spring.cloud.alibaba.version>
    <springdoc.openapi.version>2.2.0</springdoc.openapi.version> <!-- 用于在 Spring Web MVC 中生成和展示 OpenAPI 文档的库-->
    <tika.version>1.21</tika.version>   <!--用于提取和解析各种文档格式的内容-->
    <transmittable-thread-local.version>2.14.5</transmittable-thread-local.version> <!--用于在多线程环境中传递线程本地变量的工具类-->
    <!--  Job 定时任务相关  -->
    <xxl-job.version>2.4.0</xxl-job.version>
    <sentinel-core.version>1.8.6</sentinel-core.version>
    <hibernate-validator.version>8.0.1.Final</hibernate-validator.version>
    <easyexcel.version>3.3.4</easyexcel.version>
    <commons-net.version>3.11.1</commons-net.version>
    <aviator.version>5.4.1</aviator.version>
    <jjwt.version>0.12.5</jjwt.version>
    <minio.version>8.5.5</minio.version>
    <mockito-core.version>5.12.0</mockito-core.version>
    <flowable-engine.version>7.0.0</flowable-engine.version>

  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.apache.skywalking</groupId>
        <artifactId>apm-toolkit-trace</artifactId>
        <version>${apm-toolkit-trace.version}</version>
      </dependency>
      <dependency>
        <groupId>commons-beanutils</groupId>
        <artifactId>commons-beanutils</artifactId>
        <version>${commons-beanutils.version}</version>
      </dependency>
      <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>${commons-io.version}</version>
      </dependency>
      <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
        <version>${commons-codec.version}</version>
      </dependency>
      <dependency>
        <groupId>com.dameng</groupId>
        <artifactId>DmJdbcDriver18</artifactId>
        <version>${dm8.version}</version>
      </dependency>
      <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-3-starter</artifactId>
        <version>${druid.version}</version>
      </dependency>
      <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
        <version>${dynamic.datasource.version}</version>
      </dependency>
      <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>${jackson.version}</version>
      </dependency>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
      </dependency>
      <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>${hutool.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.tika</groupId>
        <artifactId>tika-core</artifactId>
        <version>${tika.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>${poi.version}</version>
      </dependency>
      <dependency>
        <groupId>org.jdom</groupId>
        <artifactId>jdom</artifactId>
        <version>${jdom.version}</version>
      </dependency>
      <dependency>
        <groupId>com.alibaba.fastjson2</groupId>
        <artifactId>fastjson2</artifactId>
        <version>${fastjson.version}</version>
      </dependency>
      <dependency>
        <groupId>com.alibaba.fastjson2</groupId>
        <artifactId>fastjson2-extension</artifactId>
        <version>${fastjson.version}</version>
      </dependency>
      <dependency>
        <groupId>com.alibaba.fastjson2</groupId>
        <artifactId>fastjson2-extension-spring6</artifactId>
        <version>${fastjson.version}</version>
      </dependency>
      <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-bom</artifactId>
        <version>${grpc-bom.version}</version>
        <scope>import</scope>
        <type>pom</type>
      </dependency>
      <dependency>
        <groupId>com.google.protobuf</groupId>
        <artifactId>protobuf-bom</artifactId>
        <version>${protobuf-bom.version}</version>
        <scope>import</scope>
        <type>pom</type>
      </dependency>
      <dependency>
        <groupId>net.devh</groupId>
        <artifactId>grpc-spring-boot-starter</artifactId>
        <version>${grpc-spring-boot.version}</version>
      </dependency>
      <dependency>
        <groupId>net.devh</groupId>
        <artifactId>grpc-client-spring-boot-starter</artifactId>
        <version>${grpc-spring-boot.version}</version>
      </dependency>
      <dependency>
        <groupId>net.devh</groupId>
        <artifactId>grpc-server-spring-boot-starter</artifactId>
        <version>${grpc-spring-boot.version}</version>
      </dependency>
      <!-- 适配grpc-spring-boot-starter -->
      <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-core</artifactId>
        <version>${micrometer.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-client-java</artifactId>
        <version>${rocketmq.spring.client.version}</version>
        <exclusions>
          <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
          </exclusion>
        </exclusions>
      </dependency>
      <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
        <version>${knife4j.version}</version>
      </dependency>
      <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok-mapstruct-binding</artifactId>
        <version>${lombok-mapstruct-binding.version}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>${spring-boot.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>${spring-cloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-dependencies</artifactId>
        <version>${spring.cloud.alibaba.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
        <version>${springdoc.openapi.version}</version>
      </dependency>

      <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>transmittable-thread-local</artifactId>
        <!--  解决 ThreadLocal 父子线程的传值问题  -->
        <version>${transmittable-thread-local.version}</version>
      </dependency>

      <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>${mybatis-spring.version}</version>
      </dependency>
      <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        <version>${mybatis-plus.version}</version>
      </dependency>
      <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>${page-helper.version}</version>
      </dependency>
      <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${mapstruct.version}</version>
      </dependency>
      <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-processor</artifactId>
        <version>${mapstruct.version}</version>
      </dependency>
      <dependency>
        <groupId>jakarta.persistence</groupId>
        <artifactId>jakarta.persistence-api</artifactId>
        <version>${jakarta-persistence-api.version}</version>
      </dependency>
      <dependency>
        <groupId>com.alicp.jetcache</groupId>
        <artifactId>jetcache-starter-redis</artifactId>
        <version>${jetcache.version}</version>
      </dependency>
      <dependency>
        <groupId>com.github.penggle</groupId>
        <artifactId>kaptcha</artifactId>
        <version>${kaptcha.version}</version>
      </dependency>
      <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache</artifactId>
        <version>${ehcache.version}</version>
      </dependency>
      <dependency>
        <groupId>com.belerweb</groupId>
        <artifactId>pinyin4j</artifactId>
        <version>${pinyin4j.version}</version>
      </dependency>
      <dependency>
        <groupId>com.plumelog</groupId>
        <artifactId>plumelog-logback</artifactId>
        <version>${plumelog.version}</version>
      </dependency>
      <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson-spring-boot-starter</artifactId>
        <version>${redisson.version}</version>
      </dependency>
      <dependency>
        <groupId>com.xuxueli</groupId>
        <artifactId>xxl-job-core</artifactId>
        <version>${xxl-job.version}</version>
      </dependency>
      <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-core</artifactId>
        <version>${sentinel-core.version}</version>
      </dependency>

      <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>${hibernate-validator.version}</version>
      </dependency>

      <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>${easyexcel.version}</version>
      </dependency>
      <dependency>
        <groupId>commons-net</groupId>
        <artifactId>commons-net</artifactId>
        <version>${commons-net.version}</version>
      </dependency>
      <dependency>
        <groupId>com.googlecode.aviator</groupId>
        <artifactId>aviator</artifactId>
        <version>${aviator.version}</version>
      </dependency>
      <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>${jjwt.version}</version>
      </dependency>
      <dependency>
        <groupId>io.minio</groupId>
        <artifactId>minio</artifactId>
        <version>${minio.version}</version>
      </dependency>
      <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>${mockito-core.version}</version>
        <scope>test</scope>
      </dependency>
      <!-- https://mvnrepository.com/artifact/org.flowable/flowable-engine -->
      <dependency>
        <groupId>org.flowable</groupId>
        <artifactId>flowable-engine</artifactId>
        <version>${flowable-engine.version}</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
</project>

        其实在搭建父工程时,我并不太理解这个父工程的意义,感觉有它没它区别不大。但是在撰写这篇博客的背景章节时,我幡然醒悟,父工程不就是整理了一套可以搭配起来使用的组件及版本清单吗?有了父工程做背书,子工程只需要根据自己的需求引入父工程指定的依赖即可,而不用担心引入的依赖会不会和其他依赖冲突,会不会因此导致服务无法启动,会不会引入漏洞,这不就从根本上简化了操作吗?

      三、子工程调试

        创建好了父工程,并不代表它一定是可用的。上文已经说过,在整理依赖组件时,所有的版本都是能取最新取最新,但有可能很多最新版的依赖相互之间并不兼容,所以还需要进行一定的调试。于是我先对父工程进行mvn install,再引用父工程创建了一个子工程,如下:

        主要是编写上图中的pom文件,注意要引用父工程,并把所有的依赖包都转过来。有条件的还可以再写个增删改查功能,如下:

<!--CommonMapper.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.leixi.leixihub.dao.CommonMapper">

    <select id="getDataBySql" resultType="java.util.Map">
        ${sql}
    </select>

    <update id="updateDataBySql">
        ${sql}
    </update>
</mapper>

//这里是 CommonMapper.java

package com.leixi.leixihub.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;

/**
 *
 * @author leixiyueqi
 * @since 2024/8/5 19:39
 */
@Mapper
public interface CommonMapper extends BaseMapper {
    List<Map<String, Object>> getDataBySql(@Param("sql") String sql);
    
    void updateDataBySql(@Param("sql") String sql);
}

// 这里是Controller方法

    @GetMapping("/getDataBySql")
    public Object getDataBySql(@RequestParam(value = "sql") String sql) {
        return commonMapper.getDataBySql(sql);
    }

      四、接口测试

        有了这些代码和依赖,只要项目能成功启动,增删改查能顺利执行,就说明这个架子初步搭成了。

      五、依赖包冲突解决和版本调整

        虽说项目可以起来,但这并不代表这个父工程是安全可用的,主要原因有两个:

                 1)引用的依赖包也不一定是安全,可能存在漏洞。

                 2)各依赖包之间可能存在依赖冲突的问题。

        咱们基于以上两点,对这份依赖文件进行一次复查,逐个解决和排查问题。

        1、对于存在漏洞,或者依赖的资源有漏洞的包,需要更换其版本,或者更换其依赖的子包的版本,或者替换另一个jar包。对于这些有问题的包,Idea的pom文件里都会有较明显的提示,一般标黄底的都是有些毛病的,如下图:

         2、检查和处理依赖冲突,点击Idea2023右上角的图标,可以查看当前有冲突的依赖信息:

        如下图,minio-8.5.5和jetcache-starter-redis-2.7.5都依赖checker-qual包,但是使用的版本不一样:

        这种情况下,有两种解决方案,

        1) 降低jetcache-starter-redis的版本号,或者提升minio的版本号,让它们依赖的checker-qual变得一样,可以在https://mvnrepository.com/中查询各包依赖的版本号,如下:

        2)排除minio中低版本的checker-qual依赖,这需要更改pom.xml里的配置,如下:

      六、回写父工程

        根据上面的方法把依赖信息调整之后,一定要记得把相关变化都更新到父工程leixi-hub-parent里,避免每个子类都要进行类似的配置和调整。测试证明,只要父工程配好了<exclusions>,子工程里不用做这些配置,也不会显示冲突项。

      SpringBoot2转3的方法

        除了上文中说到了升级相关环境、依赖的版本,在对实际工程进行升级时,尤其是对以前的SpringBoot2.X升级到3.X时,还需要注意以下方面:

      1. 升级 JDK 17

        Spring Boot 3.0 需要 Java 17 作为最低版本。如果当前使用的是 Java 8 或 Java 11,则需要在 Spring Boot 迁移之前升级 JDK。

      2. 升级到 Spring Boot 3

        查看项目及其依赖项的状态后,要升级到 Spring Boot 3.0 的最新维护版本。对于不需要父工程的项目,可以这么写:

<parent>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-parent</artifactId>
     <version>3.2.0</version>
</parent>

      3. 配置属性迁移

        在 Spring Boot 3.0 中,一些配置属性被重命名/删除,开发人员需要相应地更新其 application.properties/application.yml为了快速实现这一点,Spring Boot 提供了一个 spring-boot-properties-migrator 模块。咱可以通过将以下内容添加到 Maven pom.xml 来添加迁移器:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-properties-migrator</artifactId>
  <scope>runtime</scope>
</dependency>

      4. 升级到 Jakarta EE

        由于 Java EE 已更改为 Jakarta EE,Spring Boot 3.x 的所有依赖项 API 也从 Java EE 升级为 Jakarta EE。代码中需要将所有 javax 的 imports 都替换为 jakarta。具体如下:

javax.persistence.*   -> jakarta.persistence.*
javax.validation.*    -> jakarta.validation.*
javax.servlet.*       -> jakarta.servlet.*
javax.annotation.*    -> jakarta.annotation.*
javax.transaction.*   -> jakarta.transaction.*

      5. 调整ConstructorBinding注解

        @ConstructorBinding@ConfigurationProperties 类的类型级别不再需要,应将其删除。

当一个类或记录有多个构造函数时,它仍然可以在构造函数上使用,以指示应使用哪一个构造函数进行属性绑定。

      6. 尾部斜杠URL匹配更改

        从 Spring Framework 6.0 开始,尾部斜杠匹配配置选项已为 deprecated,其默认值设置为 false。这意味着以前,以下控制器将匹配GET /healthGET /health/

@RestController
public class HealthController {

  @GetMapping("/health")
  public String health() {
    return "Application is Working";
  }

}

      7. RestTemplate 调整

        Spring Framework 6.0 中已删除对 Apache HttpClient 的支持,现在由 org.apache.httpcomponents.client5:httpclient5 取代。如果 HTTP 客户端行为存在问题,则 RestTemplate 可能会回退到 JDK 客户端。org.apache.httpcomponents:httpclient 可以由其他依赖项传递传递,因此在应用程序可能依赖此依赖项而不声明它。

下面是迁移后的RestTemplate示例:


import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.util.Timeout;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

/**
 *
 * @author leixiyueqi
 * @since 2024/8/5 21:39
 */
@Configuration
public class RestTemplateConfig {

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return buildTemplate(10000, 10000,3);
    }


    private RestTemplate buildTemplate(long requestTimeout, long connectTimeout, int retryTimes) {
        final SSLConnectionSocketFactory sslConnectionSocketFactory = SSLConnectionSocketFactoryBuilder.create()
                .build();
        final PoolingHttpClientConnectionManager manager = PoolingHttpClientConnectionManagerBuilder.create()
                .setSSLSocketFactory(sslConnectionSocketFactory)
                .build();

        final CloseableHttpClient closeableHttpClient = HttpClients.custom()
                .setDefaultRequestConfig(RequestConfig.custom().setConnectionRequestTimeout(Timeout.ofMilliseconds(requestTimeout))
                        .setConnectTimeout(Timeout.ofMilliseconds(connectTimeout)).build())
                //.setRetryStrategy(new DefaultHttpRequestRetryStrategy(retryTimes, NEG_ONE_SECOND))
                .setConnectionManager(manager)
                .build();

        final HttpComponentsClientHttpRequestFactory componentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        componentsClientHttpRequestFactory.setHttpClient(closeableHttpClient);

        final RestTemplate restTemplate = new RestTemplate(componentsClientHttpRequestFactory);
        return  restTemplate;
    }
}

      8. 升级 Spring Security

        Spring Boot 3.0 已升级到 Spring Security 6.0。因此,WebSecurityConfigurerAdapter 已被弃用。 Spring鼓励用户转向基于组件的安全配置。可使用 Spring Security lambda DSL 和方法 HttpSecurity#authorizeHttpRequests 来定义自己的授权规则。

        下面是使用 WebSecurityConfigurerAdapter 的示例配置,它通过 HTTP Basic 保护所有端点:

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authz) -> authz
                .anyRequest().authenticated()
            )
            .httpBasic(withDefaults());
    }

}

         展望未来,推荐的方法是注册一个 SecurityFilterChain bean:

@Configuration
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authz) -> authz
                .anyRequest().authenticated()
            )
            .httpBasic(withDefaults());
        return http.build();
    }

}

     9. Spring Kafka 模板升级

        KafkaTemplate 方法现在返回 CompleteableFuture 而不是 ListenableFuture,后者已被弃用。Spring Boot 2.x 中带有 ListenableFuture 的 Kafka 模板:

private RoutingKafkaTemplate routingKafkaTemplate;

public void send(){
    ListenableFuture<SendResult<Object,Object>> future = routingKafkaTemplate.send("Message","topic");

    future.addCallback(new ListenableFutureCallback<>() {
        @Override
        public void onFailure(Throwable ex) {
            log.error(ex);
        }

        @Override
        public void onSuccess(SendResult<Object, Object> result) {
            log.info("success");
        }
    });
}

        Spring Boot 3.x 中带有 CompletableFuture 的 Kafka 模板:

private RoutingKafkaTemplate routingKafkaTemplate;

public void send() {
    CompletableFuture<SendResult<Object, Object>> future = routingKafkaTemplate.send("Message", "topic");
    future.thenAccept(log::info)
            .exceptionally(exception -> {
                log.error(exception);
                return null;
            });
}

      10. Spring Doc OpenAPI 升级

        springdoc-openapi用于为Spring Boot 项目自动生成 API 文档。 springdoc-openapi的工作原理是在运行时检查应用程序,以根据 spring 配置、类结构和各种注释推断 API 语义。对于 spring-boot 3 支持,请确保使用 springdoc-openapi v2。对于 WebMVC 项目,需要在 pom.xml. 文件中包含以下依赖项。

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.3.0</version>
</dependency>

        对于 WebFlux 项目,您需要在 pom.xml. 文件中包含以下依赖项。

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
    <version>2.3.0</version>
</dependency>

参考资料

        以下是相关参考资料,感谢大佬们的倾情整理。

        参考0jdk8升级JDK17避坑指南

        参考1:重磅!Spring Boot 2.7 正式发布

        参考2:hutool-希望Hutool能支持下JDK8~JDK17的所有版本

        参考3:一文详解|从JDK8飞升到JDK17,再到未来的JDK21

        参考4:从 Java 8 升级到 Java 17 踩坑全过程,建议收藏!

        参考5:老卫waylau-JDK

        参考6:java - Spring Boot 2.x 到 3.2 的全面升级指南

     后记

        抛开过程中的困难和曲折不说,这是一次酣畅淋漓,难得且难忘的一次机会,一个中小型公司终其一生,能有几次这么升级框架的机会?又有多少程序员能有这样的经历?讲真,我是很感激老大能给我这个机会的。通过这次升级,我对于SpringBoot项目的整体架构,父子工程关系等都有了更深的认识,对依赖整理,漏洞排除也有了相关的积累。苦点累点没什么,获得的成就感却是满满的,将来,公司所有的项目,都将在整理的工程基础上进行建设,我不就是名副其实的奠基人了吗?(可把我给牛批坏了!)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1993246.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Fiddler代理后浏览器无法上网啥情况

当使用Fiddler作为代理服务器后&#xff0c;浏览器无法上网的情况通常是由以下几个原因造成的&#xff1a; 代理服务器配置不正确&#xff1a; 确保在浏览器或其他客户端中正确配置了Fiddler作为代理服务器。代理服务器地址应为运行Fiddler的计算机的局域网IP地址&#xff0c;端…

【Canvas与艺术】黄色立体感放射光芒五角星

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>黄色立体感放射光芒五角星</title><style type"text/c…

黄金走势分析及经济前景展望

黄金市场动态 近期&#xff0c;全球经济不确定性加剧&#xff0c;使得黄金市场备受关注。美国国债收益率的上涨进一步支撑了美元&#xff0c;推动黄金价格进入高位震荡阶段。尽管黄金在短期内受到波动的影响&#xff0c;但长期避险资产的吸引力仍不容忽视。 经济数据与黄金走势…

Spring---AOP(面向切面编程)

AOP(Aspect-Oriented Programming: 面向切面编程)&#xff1a;将那些与业务无关&#xff0c;却为业务模块所共调用的逻辑&#xff08;例如事务处理、日志管理、权限控制等&#xff09;封装抽取成一个可重用的模块&#xff0c;这个模块被命名为“切面”&#xff08;Aspect&#…

Promethues Metrics

Metrics Metrics可分为三部分&#xff1a; HELP 描述metric作用TYPE metric类别 TYEP Counter 某个事件发生的次数数字只能增长 Total reuqests Total ExceptionsGauge 描述当前值可以上升或下降 CurrentCPU Utilization Available System Memory Number of concurren…

萌新的Java入门日记19

Vue真恶心&#xff01;&#xff01;&#xff01;呜呜呜 5.配置代理 为了避免因后端服务器迁移造成的麻烦&#xff0c;在 vite.config.js 文件中配置如下代码&#xff1a; export default defineConfig({plugins: [vue()],server:{// 配置vite冷启动项目自动使用浏览器访问首页…

【中项】系统集成项目管理工程师-第10章 项目整合管理-10.6实施整体变更控制

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&…

4个免费好用的免扣素材神器!png素材根本用不完!

你是否曾为找不到合适的PPT素材而头疼&#xff1f;模糊的图片、带水印的模板&#xff0c;还有那些让人抓狂的素材搜索难题。别急&#xff0c;今天就来给大家安利四款我私藏的PPT素材神器&#xff0c;让你的PPT设计从此变得简单又高效&#xff01; 一、千鹿设计助手 — AI免抠图…

算法力扣刷题记录 七十【70. 爬楼梯及算法性能分析:时间复杂度和空间复杂度】

前言 动态规划章节第二篇。记录 七十【70. 爬楼梯】 一、题目阅读 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;2 解释&#xf…

SQL注入sqli-labs-master关卡三

第三关如下&#xff1a; 查看该关卡的代码发现其与关卡一和关卡二的不同之处在于id($id)这里。 那么我们输入?id1或?id2)--都能用来判断是字符型还是数字型注入。 接着输入?id1) order by 3--检查它的列数。检查到4报错&#xff0c;说明只有三列。 输入?id-1) union select…

02_快速启动 Demo 创建 Electron 项目、electron-forge 搭建一个 electron 项目、手动创建electron项目

快速启动 Demo 创建 Electron 项目 一、克隆一个仓库、快速启动一个项目二、electron-forge 搭建一个 electron 项目三、手动搭建一个 electron 项目四、开发工具中配置 Eslint 一、克隆一个仓库、快速启动一个项目 要使用 git 的话首先电脑上面需要安装 git //克隆示例项目的…

Cpp中的this指针--复习记录

1.什么是this指针? 每个类都有一个this指针&#xff0c;我们的非静态成员函数可以通过这个this指针来操作对象的成员属性。this指针存储的就是类的实例的地址&#xff0c;this指针时时刻刻指向的都是这个实例对象本身。 由下图可知: 我在主函数中栈上创建了一个类的实例(由操…

数据规模介绍

batch_size 2 1829*2 3658张图片 FSC147数据集介绍 train 3659 val 1286 test 1190

xxl-job 源码梳理(2)-服务端

目录 1. 控制面的接口2.手动触发任务2. 定时任务的实现 1. 控制面的接口 服务端包含xxl-job的管理端&#xff0c;页面上的接口后端一系列的controller接口 appName是一个核心概念&#xff0c;它是指执行器应用的名称&#xff0c;appName是执行器的唯一标识 页面上的接口&#…

出行365:依托分布式数据库,让出行无忧 | OceanBase案例

*本文首发自“新华社环球”杂志&#xff0c;作者张海鑫 每年的暑期旅游旺季&#xff0c;都会触发一轮轮的文旅消费的热潮&#xff0c;对于互联网出行服务行业而言&#xff0c;这既是一场盛大的狂欢&#xff0c;也是对其综合实力的严峻考验。 然而&#xff0c;自去年暑假起&…

Email发送接口安全性保障策略?如何优化?

Email发送接口的高级功能&#xff1f;怎么有效利用邮件API接口&#xff1f; Email发送接口的安全性对于防止数据泄露、滥发垃圾邮件和恶意攻击至关重要。AokSend将探讨Email发送接口的安全性保障策略&#xff0c;帮助开发者和企业确保其电子邮件通信的安全性和可靠性。 Email…

智能猫砂盆买错有什么危害?深度解析三款热门爆款产品!

作为一名家里还有小猫在等待的上班族&#xff0c;我们经常因为需要加班或频繁出差而忙碌得不可开交&#xff0c;导致我们很容易忽略猫咪的厕所环境和健康安全&#xff0c;每次急匆匆地出门&#xff0c;都发现自己似乎忘了给猫咪及时铲屎。但是大家要知道&#xff0c;不及时清理…

为人处世,“会说话”是一生的修行

职场上&#xff0c;常常存在这样一种现象&#xff1a;“会干活的&#xff0c;不如会说的。” 学会“好好说话”、“说正确的话”“说让人舒服的话”成为一生必须要面对的修行。 01 丰厚的学养&#xff0c;是“会说话”的根基。 同一句话&#xff0c;“会说话”的人&#xf…

XXXForm组件

效果展示 代码 XXXForm <template><div class"search-container"><el-form ref"formRef" class"form_is_hidden" :model"form" v-bind"formAttrs"><el-row :gutter"20" class"search…

一文带你快速了解——LVS负载均衡集群

前言&#xff1a; Internet的飞速发展给网络带宽和服务器带来巨大的挑战。从网络技术的发展来看&#xff0c;网络带宽的增长远高于处理器速度和内存访问速度的增长。对用硬件和软件方法实现高可伸缩、高可用网络服务的需求不断增长。针对高可伸缩、高可用网络服务的需求&#x…