• SpringBoot2.7升级到3.0的实践分享


    背景

    最近把项目中的技术框架做一次升级,最重要的就是SpringBoot从2.7.x升级到3.0.x,当然还会有一些周边的框架也会连带着升级,比如Mybatis Plus,SpringCloud等,话不多说直接看看有哪些事情要做。

    具体事项

    主要分两类,第一类是单纯的提升版本,主要如下:

    1.jdk升级

    SpringBoot3需要jdk17以上,看下官网的介绍。

     

    2.构建工具Maven需要升级,3.5+以上。

    3.SpringCloud需要升级,原来是2021.0.3,现在决定升级到2022.0.4,中途也尝试过SpringCloud暂时不升级,但是启动失败了,果断决定升级,二者的对照关系请参考https://start.spring.io/actuator/info。

    4.Mybatis,Mybatis-plus需要升级,分别升级到3.5.13,3.5.3.2。

    第二类是由于版本升级带来的一些配置和代码的变更,主要如下:

    1. javax.需要批量替换

    import javax.annotation. 替换为import jakarta.annotation.

    import javax.servlet.替换为import jakarta.servlet.

    这个我也是有点不解,为什么连路径都要做迁移,这个会有一些连带的改变,比如使用的一些第三方jar使用的是javax.servlet/annotation,那我们就需要找到其高版本看是否切换到了jakarta.servlet/annotation,如果不支持那我们的整体升级可能都要叫停,刚开始也想过找到一些老的jar保证编译不报错,但是运行的时候发现根本不行,因为SpringBoot,Spring等在底层使用的时候都切到了jakarta,为了适应新版本框架必须要改,庆幸的是用的第三方jar都有相应的高版本支持。

    2.Spring Cloud OpenFeign升级以后,FeignContext不存在

    之前有框架代码中获取FeignBean的需求,代码如下:

    1
    2
    3
    4
    public static T getFeignBean(String beanName, Class tClass) {
          FeignContext feignContext = (FeignContext)getBean("feignContext");
          return feignContext.getInstance(beanName, tClass);
      }

    需要调整为FeignClientFactory:

    1
    2
    3
    4
    public static T getFeignBean(String beanName, Class tClass) {
        FeignClientFactory feignContext = (FeignClientFactory)getBean("feignContext");
        return feignContext.getInstance(beanName, tClass);
    }

    3.redis配置修改

    之前是spring.redis.xxx,现在需要修改为spring.data.redis.xxx。

    4.mybatis plus由3.2.0升级到3.5.3.2以后带来的几个调整

    4.1 id-type: UUID调整为id-type: ASSIGN_UUID

    4.2 拦截器路径以及构造方式的变化

    之前如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Configuration
    public class MybatisPlusConfig {
      /**
       * 分页插件
       */
      @Bean
      public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
      }

    现在如下:

    复制代码
    @Configuration
    public class MybatisPlusConfig {
      /**
       * 分页插件
       */
      @Bean
      public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
      }
    复制代码

    4.3 selectCount返回值由Integer变更为Long

    这个好改,直接按照idea的提示改成Math.toIntExact(xxxMapper.selectCount)。

    带来的好处

    先说说我最直观的一个好处,jdk由11升级到17以后空指针的异常提示清晰多了,可以明确的知道是哪个对象空,分别以jdk11和jdk17为例测试一段链式调用的NPE异常,测试代码如下:

    复制代码
    static class User{
            int age;
            public int getAge(){
                return age;
            }
    }
    
    @Test
    public void testNPE(){
        List list = new ArrayList();
        list.add(null);
        list.stream().filter(o -> o.getAge() >9).collect(Collectors.toList())
    }
    复制代码

    分别看下在jdk11和jdk17下的报错提示。

    jdk11

    复制代码
    java.lang.NullPointerException
      at NpeTest.lambda$testNPE$0(NpeTest.java:41)
      at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:176)
      at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655)
      at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
      at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
      at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
      at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
      at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
      at NpeTest.testNPE(NpeTest.java:41)
    复制代码

    jdk17

    复制代码
    java.lang.NullPointerException: Cannot invoke "NpeTest$User.getAge()" because "o" is null
      at NpeTest.lambda$testNPE$0(NpeTest.java:41)
      at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:178)
      at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
      at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
      at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
      at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
      at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
      at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
    复制代码

    其次就是一次老生常谈的好处,比如性能提升,安全加固等,我了解的还很浅薄,待我慢慢发掘以后再跟大家分享。

    推荐阅读

    https://spring.io/blog/2022/05/24/preparing-for-spring-boot-3-0 

    https://docs.spring.io/spring-boot/docs/3.0.10/reference/htmlsingle/#getting-help

    https://github.com/spring-projects-experimental/spring-boot-migrator

    https://blog.csdn.net/dreamcatcher1314/article/details/131137388

     

     

     

     

  • 相关阅读:
    VLAN trunk扩展 MUXVLAN 原理与实验
    【ELK 使用指南 3】Zookeeper、Kafka集群与Filebeat+Kafka+ELK架构(附部署实例)
    学习永不止步!誉天9月开班计划来啦!
    二刷力扣--数组
    SpringSecurity Oauth2实战 - 07 整合Redis延长页面自动退出登录时间
    数学建模——微分方程
    sql 注入, 报错型注入
    05 - FFmpeg 提取 PCM 音频裸数据
    vue/html input 读取 json数据
    Python---数据容器分类及通用操作
  • 原文地址:https://www.cnblogs.com/chopper-poet/p/17747086.html