SpringBoot2.6.6+JDK8升级到SpringBoot3.4.2+JDK17踩坑

目录

1、背景

2、我看到的主要改造内容

2.1、下载JDK17以上的版本,本地安装和项目配置

2.1、包替换

2.2、启动中碰到的异常

2.2.1 Base64包已废弃

2.2.2 nacos配置失效

2.2.2.1 排错过程:

2.2.2.2 有两种方式解决

2.2.2.3 问题来了

2.2.3 redis配置失效

2.3 docker环境修改

2.4 全流程验证


1、背景

老项目,jdk版本问1.8,springboot版本为2.6.6,升级jdk和spring全家桶

2、我看到的主要改造内容

  主要过程就是解决大的问题,然后不断的启动,解决启动中的报错

2.1、下载JDK17以上的版本,本地安装和项目配置

  嗯嗯,这个我这么菜的人都会,我相信人均架构师的大家都会~~~

2.1、包替换

原包替换后的包
javax.annotation.jakarta.annotation.
javax.servlet.jakarta.servlet.
javax.persistence.jakarta.persistence.
javax.validation.jakarta.validation.
javax.transaction.*jakarta.transaction.*

直接在idea中 control + shift + r全量替换即可

2.2、启动中碰到的异常

2.2.1 Base64包已废弃

启动时编译发现报错:

D:\code\jaylli-backend-server\src\main\java\com\jaylli\backend\client\order\OrderUtil.java:3:16
java: 找不到符号
  符号:   类 BASE64Encoder
  位置: 程序包 sun.misc

修改新的import路径:

import sun.misc.BASE64Encoder  变更为  import java.util.Base64;

调用的地方修改成对应的调用方式,比如:

new BASE64Encoder().encode(out.toByteArray());
修改为:
Base64.getEncoder().encodeToString(out.toByteArray());

2.2.2 nacos配置失效

2.2.2.1 排错过程

解决编译报错之后,始终无法读取到nacos的配置,开始排查nacos配置的问题,

1、确认是否连接上nacos:尝试手动修改错误的redis配置,比如错误的ip,错误的username或password,发现报错信息没有任何变化(我预期应该是会报nacos失败,账号密码错误这种异常,结果是啥都没有)

2、对比升级之前修改错误的redis配置启动,发现是有一些异常的

3、得出结论,应该是升级springboot之后无法加载nacos配置

查阅资料发现,SpringBoot3.*开始:

1、 配置加载机制变化
    Spring Boot 3.0+ 默认不再自动加载 bootstrap.yml 文件
    需要通过 spring.config.import 显式引入外部配置

2.2.2.2 有两种方式解决

方式1:application.yml中添加显式nacos配置:

spring:
  config:
    import:
      - nacos:${app_name}.yml

将nacos相关的配置移到application.yml

spring:
  cloud:
    nacos:
      server-addr: ${app_addr}
      username: ${username}
      password: ${password}
      config:
        namespace: ${namespace}
        group: ${group}
        file-extension: yml

喜大普奔,重启生效!!

方式二:继续使用bootstrap.yml,需要引用bootstrap的starter

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>

bootstrap.yml中加入显式引入nacos配置:

spring:
  config:
    import:
      - nacos:${app_name}.yml

喜大普奔,重启生效~!~~~!!!

2.2.2.3 问题来了

    使用方式2时,想尝试在application.yml中加入显式引入nacos的配置(spring.config.import),然后启动,竟然报错了:

2026-01-04 14:59:51,817 [Thread-2] WARN  c.alibaba.nacos.common.executor.ThreadPoolManager:56 - [ThreadPoolManager] Destruction of the end
Exception in thread "main" java.lang.IllegalStateException: java.lang.IllegalStateException: Logback configuration error detected: 
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[CONFIG_LOG_FILE] - 'File' option has the same value "C:\Users\Administrator/logs/nacos/config.log" as that given for appender [CONFIG_LOG_FILE] defined earlier.
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[CONFIG_LOG_FILE] - Collisions detected with FileAppender/RollingAppender instances defined earlier. Aborting.
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[CONFIG_LOG_FILE] - For more information, please visit https://logback.qos.ch/codes.html#earlier_fa_collision
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[NAMING_LOG_FILE] - 'File' option has the same value "C:\Users\Administrator/logs/nacos/naming.log" as that given for appender [NAMING_LOG_FILE] defined earlier.
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[NAMING_LOG_FILE] - Collisions detected with FileAppender/RollingAppender instances defined earlier. Aborting.
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[NAMING_LOG_FILE] - For more information, please visit https://logback.qos.ch/codes.html#earlier_fa_collision
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[REMOTE_LOG_FILE] - 'File' option has the same value "C:\Users\Administrator/logs/nacos/remote.log" as that given for appender [REMOTE_LOG_FILE] defined earlier.
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[REMOTE_LOG_FILE] - Collisions detected with FileAppender/RollingAppender instances defined earlier. Aborting.
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[REMOTE_LOG_FILE] - For more information, please visit https://logback.qos.ch/codes.html#earlier_fa_collision
	at org.springframework.boot.context.logging.LoggingApplicationListener.initializeSystem(LoggingApplicationListener.java:347)
	at org.springframework.boot.context.logging.LoggingApplicationListener.initialize(LoggingApplicationListener.java:298)
	at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEnvironmentPreparedEvent(LoggingApplicationListener.java:246)
	at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEvent(LoggingApplicationListener.java:223)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:185)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:178)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:156)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:138)
	at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136)
	at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:81)
	at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:64)
	at java.base/java.lang.Iterable.forEach(Iterable.java:75)
	at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118)
	at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:112)
	at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:63)
	at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:353)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:313)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350)
	at com.yifeng.cashier.backend.ApplicationStartUp.main(ApplicationStartUp.java:23)
Caused by: java.lang.IllegalStateException: Logback configuration error detected: 
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[CONFIG_LOG_FILE] - 'File' option has the same value "C:\Users\Administrator/logs/nacos/config.log" as that given for appender [CONFIG_LOG_FILE] defined earlier.
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[CONFIG_LOG_FILE] - Collisions detected with FileAppender/RollingAppender instances defined earlier. Aborting.
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[CONFIG_LOG_FILE] - For more information, please visit https://logback.qos.ch/codes.html#earlier_fa_collision
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[NAMING_LOG_FILE] - 'File' option has the same value "C:\Users\Administrator/logs/nacos/naming.log" as that given for appender [NAMING_LOG_FILE] defined earlier.
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[NAMING_LOG_FILE] - Collisions detected with FileAppender/RollingAppender instances defined earlier. Aborting.
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[NAMING_LOG_FILE] - For more information, please visit https://logback.qos.ch/codes.html#earlier_fa_collision
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[REMOTE_LOG_FILE] - 'File' option has the same value "C:\Users\Administrator/logs/nacos/remote.log" as that given for appender [REMOTE_LOG_FILE] defined earlier.
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[REMOTE_LOG_FILE] - Collisions detected with FileAppender/RollingAppender instances defined earlier. Aborting.
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[REMOTE_LOG_FILE] - For more information, please visit https://logback.qos.ch/codes.html#earlier_fa_collision
	at org.springframework.boot.logging.logback.LogbackLoggingSystem.reportConfigurationErrorsIfNecessary(LogbackLoggingSystem.java:291)
	at org.springframework.boot.logging.logback.LogbackLoggingSystem.loadConfiguration(LogbackLoggingSystem.java:269)
	at org.springframework.boot.logging.AbstractLoggingSystem.initializeWithSpecificConfig(AbstractLoggingSystem.java:67)
	at org.springframework.boot.logging.AbstractLoggingSystem.initialize(AbstractLoggingSystem.java:58)
	at org.springframework.boot.logging.logback.LogbackLoggingSystem.initialize(LogbackLoggingSystem.java:197)
	at org.springframework.boot.context.logging.LoggingApplicationListener.initializeSystem(LoggingApplicationListener.java:335)
	... 19 more
Disconnected from the target VM, address: '127.0.0.1:12706', transport: 'socket'

报错原因:

1. 配置加载顺序差异
    bootstrap.yml:在应用启动早期阶段加载,优先级最高
    application.yml:在 SpringApplication.run() 执行时加载
2. 报错原因
    将 spring.config.import 放到 application.yml 后,Nacos 配置加载时机发生变化
    Nacos 客户端尝试加载远程配置时,日志系统尚未完全初始化完成

产生 Logback 配置冲突或重复初始化错误

解决方案:

1、配置作用
    禁用 Nacos 默认日志配置:该配置用于关闭 Nacos 客户端的默认日志配置加载
    避免日志冲突:防止 Nacos 自动加载的日志配置与应用日志配置发生冲突

2、使用场景
    当将 spring.config.import 配置从 bootstrap.yml 移至 application.yml 时
    出现 Logback 配置错误时的解决方案

3、问题解决
    在启动类main方法通过设置 System.setProperty("nacos.logging.default.config.enabled","false")
    避免 Nacos 自动加载默认日志配置文件

@SpringBootApplication
@MapperScan({"com.jaylli.**.dao"})
@EnableFeignClients
@ConfigurationPropertiesScan
public class ApplicationStartUp {

    public static void main(String[] args) {
        // 将spring.config.import配置放在bootstrap.yml中不报错
        //   放到application.yml后报错,通过引入下面的配置解决
        System.setProperty("nacos.logging.default.config.enabled","false");
        SpringApplication.run(ApplicationStartUp.class, args);
    }
}

    再一次喜大普奔,重启生效,解决日志配置冲突导致的启动异常~~~~~

4、解决方案原理分析

    通过 System.setProperty("nacos.logging.default.config.enabled","false") 在应用启动前设置系统属性(理论上来说通过其他方式也可以,比如放在启动参数里面java -Dnacos.logging.default.config.enabled=false)
        告诉 Nacos 不要加载其默认的日志配置
        避免了日志系统的重复配置和冲突问题

5、 注意事项
    这是一个临时解决方案
    建议优先考虑使用 bootstrap.yml 配置 Nacos 配置中心
    保持配置加载顺序的正确性

小结:

不管怎么样,都需要在yml文件中新增显式引入nacos配置:

spring:
  config:
    import:
      - nacos:${app_name}.yml

如果是在bootstrap.yml中新增,需要额外新增依赖包才能正常启动和加载nacos配置:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>

如果是在application.yml中新增,则不需要新增上述依赖,但是需要排除Logback 配置冲突的问题,即需要新增参数:

System.setProperty("nacos.logging.default.config.enabled","false");

2.2.3 redis配置失效

springboot2.*之后,redis的配置节点已经做了修改

1. 版本变化点
    Spring Boot 2.0+ 开始,Redis 配置节点从 spring.redis.* 变为 spring.data.redis.*
这是 Spring Boot 2.x 版本的重大配置变更

旧版本配置,springboot1.*只支持这种,springboot2.*似乎还兼容这种方式的配置:

spring:
  redis:
    host: localhost
    port: 6379
    password: your-password

新版本配置,springboot3.*开始似乎已经完全不支持旧版本的配置了,需要使用新的配置:

spring:
  data:
    redis:
      host: localhost
      port: 6379
      password: your-password

所以,用到这个配置的地方,最好都改,早改晚改都得改!!

2.2.4 反射报错

Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.lang.Object java.util.Collections$SingletonList.element accessible: module java.base does not "opens java.util" to unnamed module @3fd7a715
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
	at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178)
	at java.base/java.lang.reflect.Field.setAccessible(Field.java:172)
	at org.apache.lucene.util.RamUsageEstimator.createCacheEntry(RamUsageEstimator.java:497)
	at org.apache.lucene.util.RamUsageEstimator.measureObjectSize(RamUsageEstimator.java:455)
	at org.apache.lucene.util.RamUsageEstimator.sizeOf(RamUsageEstimator.java:333)

原因分析:

    Java 17+ 模块系统限制:java.util 模块未对匿名模块开放访问权限
    Lucene 的 RamUsageEstimator 试图通过反射访问 Collections$SingletonList 的私有字段
    安全限制:java.base 模块不向未命名模块开放 java.util 访问权限

解决方案:

方案1:

添加 JVM 参数(推荐)
--add-opens java.base/java.util=ALL-UNNAMED

方案2:自己修改业务代码,改一改实现方式:

升级Lucene版本到9.0.0:

        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-core</artifactId>
            <version>9.0.0</version>
        </dependency>

然后修改业务代码:

RamUsageEstimator.sizeOf(obj);
修改为:
RamUsageEstimator.shallowSizeOf(obj)

2.2.5 mybatis-plus报错

2026-01-07 14:02:32,015 [http-nio-8080-exec-9] ERROR c.g.f.base.exception.ExceptionLoggerPrintHandler:55 - com.gomcarter.frameworks.base.exception.ExceptionLoggerPrintHandler操作失败, url:/com.jaylli/cart/delete, method:POST, body: , ip: 0:0:0:0:0:0:0:1, Referer: null, UA: Apifox/1.0.0 (https://apifox.com), params: {}, cookie: null,header: {"content-length":"323","host":"localhost:8080","content-type":"application/json","connection":"keep-alive","accept-encoding":"gzip, deflate, br","user-agent":"Apifox/1.0.0 (https://apifox.com)","accept":"*/*"},
org.mybatis.spring.MyBatisSystemException: 
### Error updating database.  Cause: org.apache.ibatis.builder.BuilderException: The expression 'ids' evaluated to a null value.
### The error may exist in file [D:\code\jaylli\my-cart\target\classes\mybatis\cart\CartMapper.xml]
### The error may involve com.jaylli.cart.dao.CartMapper.deleteByIds
### The error occurred while executing an update
### Cause: org.apache.ibatis.builder.BuilderException: The expression 'ids' evaluated to a null value.
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:99)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:347)
	at jdk.proxy2/jdk.proxy2.$Proxy132.delete(Unknown Source)
	at org.mybatis.spring.SqlSessionTemplate.delete(SqlSessionTemplate.java:244)
	at com.baomidou.mybatisplus.core.mapper.BaseMapper.deleteByIds(BaseMapper.java:219)
	at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:732)
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$DefaultMethodInvoker.invoke(MybatisMapperProxy.java:171)
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:92)

原因分析:

mapper里面定义了一个和mybatis-plus一模一样签名的sql:

自己定义的mapper方法和对应的sql:

@Mapper
public interface MyCartMapper extends BaseMapper<CartEntity> {
    int deleteByIds(@Param("ids") List<Long> ids);
}
    <delete id="deleteByIds">
        delete from my_cart
        where id in <foreach collection="ids" separator="," open="(" close=")" item="id"> #{id} </foreach>
    </delete>

注意,自定义的  deleteByIds 方法使用的别名是 @Param("ids")

mybatis-plus定义了一个一模一样签名的方法:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.baomidou.mybatisplus.core.mapper;

import com.baomidou.mybatisplus.core.batch.BatchSqlSession;
import com.baomidou.mybatisplus.core.batch.MybatisBatch;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.override.MybatisMapperProxy;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.MybatisBatchUtils;
import com.baomidou.mybatisplus.core.toolkit.MybatisUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiPredicate;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.exceptions.TooManyResultsException;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.ognl.OgnlOps;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

public interface BaseMapper<T> extends Mapper<T> {
    
	...此处省略一万行源码...
	
    default int deleteByIds(@Param("coll") Collection<?> idList) {
        return this.deleteByIds(idList, true);
    }
	
	
	...此处省略一万行源码...
}

注意,mybatis-plus定义的相同签名的方法的入参别名是: @Param("coll")

结论:

方法冲突: 自定义的 deleteByIds 方法与 MyBatis-Plus 内置方法签名相同
参数注解: 你的自定义方法使用 @Param("ids"),但 MyBatis-Plus 内置方法使用 @Param("coll")
XML 配置: XML 中可能按 ids 引用参数,但实际调用了内置方法

解决方案:

方案1(推荐):删除自定义的sql

方案2:把自定义的方法改名

方案3:将自定义的sql的别名改成和mybatis-plus默认的一样

为什么升级之前没有问题?因为之前老版本的mybatis-plus里面默认没有这个方法,升级之后新加的

升级之前的版本

  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.5.3.2</version>

升级之后的版本

  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
  <version>3.5.10.1</version>

至此,所以启动报错都已解决~~~

2.3 docker环境修改

    这个我们有运维帮改,但是建议程序员们还是需要了解docker的一些知识,比如dockfile一般怎么写的

2.4 全流程验证

    自测+测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值