这个报错主要是Spring Boot3.0已经为所有依赖项从 ​​Java EE​​​ 迁移到 ​​Jakarta EE API​​​,导致 ​​servlet​​​ 包名的修改,Spring团队这样做的原因,主要是避免 ​​Oracle​​ 的版权问题,解决办法很简单,两步走:

1 添加 ​​jakarta.servlet​​ 依赖

<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>

  1. 修改项目内所有代码的导入依赖

修改前:
import javax.servlet.*
修改后:
import jakarta.servlet.*

二. 附带的众多依赖包升级,导致的部分代码写法过期报警

2.1 Thymeleaf升级到3.1.0.M2,日志打印的报警

14:40:39.936 [http-nio-84-exec-15] WARN  o.t.s.p.StandardIncludeTagProcessor - [doProcess,67] - [THYMELEAF][http-nio-84-exec-15][admin/goods/goods] Deprecated attribute {th:include,data-th-include} found in template admin/goods/goods, line 4, col 15. Please use {th:insert,data-th-insert} instead, this deprecated attribute will be removed in future versions of Thymeleaf.
14:40:39.936 [http-nio-84-exec-15] WARN o.t.s.p.AbstractStandardFragmentInsertionTagProcessor - [computeFragment,385] - [THYMELEAF][http-nio-84-exec-15][admin/goods/goods] Deprecated unwrapped fragment expression "admin/header :: header-fragment" found in template admin/goods/goods, line 4, col 15. Please use the complete syntax of fragment expressions instead ("~{admin/header :: header-fragment}"). The old, unwrapped syntax for fragment expressions will be removed in future versions of Thymeleaf.

可以看出作者很贴心,日志里已经给出了升级后的写法,修改如下:

修改前:
<th:block th:include="admin/header :: header-fragment"/>
修改后:
<th:block th:insert="~{admin/header :: header-fragment}"/>

2.2 Thymeleaf升级到3.1.0.M2,后端使用 ​​thymeleafViewResolver​​ 手动渲染网页代码报错

// 修改前 Spring Boot2.7:
WebContext ctx = new (request, response,
request.getServletContext(), request.getLocale(), model.asMap());
html = thymeleafViewResolver.getTemplateEngine().process("mall/seckill-list", ctx);

上述代码中针对 ​​WebContext​​ 对象的创建报错,这里直接给出新版写法

// 修改后 Spring Boot3.0:
JakartaServletWebApplication jakartaServletWebApplication = JakartaServletWebApplication.buildApplication(request.getServletContext());
WebContext ctx = new WebContext(jakartaServletWebApplication.buildExchange(request, response), request.getLocale(), model.asMap());
html = thymeleafViewResolver.getTemplateEngine().process("mall/seckill-list", ctx);

三. 大量第三方库关于 ​​Spring Boot​​​ 的 ​​starter​​ 依赖失效,导致项目启动报错

博主升级到3.0后,发现启动时,​​Druid​​​ 数据源开始报错,找不到数据源配置,便怀疑跟 ​​Spring boot​​ 3.0 更新有关

这里直接给出原因:​​Spring Boot​​​ 3.0 中自动配置注册的 ​​spring.factories​​​ 写法已废弃,改为了 ​​META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports​​​ 写法,导致大量第三方 ​​starter​​ 依赖失效

在吐槽一下,这么重要的更改在Spring官方的 ​​Spring-Boot-3.0-发布说明​​​ 中竟然没有,被放在了 ​​Spring-Boot-3.0.0-M5-发布说明​​ 中

这里给出两个解决方案:

  1. 等待第三方库适配 ​​Spring Boot​​ 3.0
  2. 按照 ​​Spring Boot​​​ 3.0要求,在项目​​resources​​​ 下新建 ​​META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports​​​ 文件,手动将第三方库的 ​​spring.factories​​​ 加到 ​​imports​​​ 中,这样可以手动修复第三方库 ​​spring boot starter​​ 依赖失效问题

四. Mybatis Plus 依赖问题

​Mybatis plus​​​ 最新版本还是3.5.2,其依赖的 ​​mybatis-spring​​​ 版本是2.2.2(​​mybatis-spring​​​ 已经发布了3.0.0版本适配 ​​Spring Boot​​​ 3.0),这会导致项目中的sql查询直接报错,这里主要是因 ​​Spring Boot​​​ 3.0中删除 ​​NestedIOException​​​ 这个类,在 ​​Spring boot​​​ 2.7中这个类还存在,给出类说明截图

这个类在2.7中已经被标记为废弃,建议替换为 ​​IOException​​​, 而 ​​Mybatis plus​​​ 3.5.2中还在使用。这里给出问题截图 ​​MybatisSqlSessionFactoryBean​​​ 这个类还在使用 ​​NestedIOException​

查看 ​​Mybatis plus​​​ 官方issue也已经有人提到了这个问题,官方的说法是 ​​mybatis-plus-spring-boot-starter​​ 还在验证尚未推送maven官方仓库,这里我就不得不动用我的小聪明,给出解决方案:

  1. 手动将原有的 ​​MybatisSqlSessionFactoryBean​​​ 类代码复制到一个我们自己代码目录下新建的 ​​MybatisSqlSessionFactoryBean​​​ 类,去掉 ​​NestedIOException​​ 依赖
  2. 数据源自动配置代码修改

@Slf4j
@EnableConfigurationProperties(MybatisPlusProperties.class)
@EnableTransactionManagement
@EnableAspectJAutoProxy
@Configuration
@MapperScan(basePackages = "ltd.newbee.mall.core.dao", sqlSessionFactoryRef = "masterSqlSessionFactory")
public class HikariCpConfig {

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}


@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
return new HikariDataSource();
}

/**
* @param datasource 数据源
* @return SqlSessionFactory
* @Primary 默认SqlSessionFactory
*/
@Bean(name = "masterSqlSessionFactory")
public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource datasource,
Interceptor interceptor,
MybatisPlusProperties properties) throws Exception {
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(datasource);
// 兼容mybatis plus的自动配置写法
bean.setMapperLocations(properties.resolveMapperLocations());
if (properties.getConfigurationProperties() != null) {
bean.setConfigurationProperties(properties.getConfigurationProperties());
}
if (StringUtils.hasLength(properties.getTypeAliasesPackage())) {
bean.setTypeAliasesPackage(properties.getTypeAliasesPackage());
}
bean.setPlugins(interceptor);
GlobalConfig globalConfig = properties.getGlobalConfig();
bean.setGlobalConfig(globalConfig);
log.info("------------------------------------------masterDataSource 配置成功");
return bean.getObject();
}

@Bean("masterSessionTemplate")
public SqlSessionTemplate masterSessionTemplate(@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}

}

到这里,项目就能够正常跑起来了