简谈Spring Boot3.0升级后的踩坑之旅,另附解决方案!
Posted Javatutouhouduan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简谈Spring Boot3.0升级后的踩坑之旅,另附解决方案!相关的知识,希望对你有一定的参考价值。
注:本文基于 newbeemall 项目升级Spring Boot3.0踩坑总结而来。
一. 编译报错,import javax.servlet.*; 不存在
这个报错主要是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>
-
修改项目内所有代码的导入依赖
修改前:
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-发布说明 中
这里给出两个解决方案:
-
等待第三方库适配 Spring Boot 3.0
-
按照 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官方仓库,这里我就不得不动用我的小聪明,给出解决方案:
-
手动将原有的 MybatisSqlSessionFactoryBean 类代码复制到一个我们自己代码目录下新建的 MybatisSqlSessionFactoryBean 类,去掉 NestedIOException 依赖
-
数据源自动配置代码修改
@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);
到这里,项目就能够正常跑起来了
总结
Spring Boot 3.0 升级带来了很多破坏性更改,把众多依赖升级到了最新,算是解决了一部分历史问题,也为了云原型需求,逐步适配 graalvm ,不管怎么样作为技术开发者,希望有更多的开发者来尝试 Spring Boot 3.0 带来的新变化。
初入gulp的踩坑之旅
初学gulp,掉进了几个坑。希望看了这篇文章能帮你躲开。
如何安装依赖
安装好npm之后,就可以用命令npm install gulp-xxx --save-dev一个包一个包的安装了。这个命令会把依赖包自动下载到工程的node_modules目录下。这样require语句就可以找到包了。
一个包一个包的安装太麻烦,怎么办?可以把所有的依赖包,写到
package.json的devDependencies里。然后执行npm install --save-dev就可以把所有依赖包全部自动安装了。用npm init可以生成一个空白的package.json。
安装依赖包的机制是NodeJs的,和gulp没有关系。所以gulp的文档是不太会将依赖是怎么来的,对于不熟悉NodeJs的人算是一个坑吧。
如何在src命令里面排除目录
形如:gulp.src(['**/*',"!dist/","!dist/**/*"])
关键在于不光要排除目录,还要把目录下的所有文件也排除。否则排除是不生效的。
如何让task串行执行
让task1依赖于task2,可以保证task2在task1之前执行,但是并不能保证task2完全执行完task1才执行。如果task2是src+dest的复制任务的话,task1不等复制完成就启动的话,是不会得到正确结果的。
让两个任务完全串行其实很简单,返回流对象就可以了。
gulp.task('copy', function() {
return gulp.src(['index.html']).pipe(gulp.dest('dist'));
});
至于加上了return返回了流对象就可以完全串行,我也不知道。大概是通过流对象可以判断出任务的执行状态吧。
最后入门的文章我推荐这一篇:http://www.cnblogs.com/2050/p/4198792.html
以上是关于简谈Spring Boot3.0升级后的踩坑之旅,另附解决方案!的主要内容,如果未能解决你的问题,请参考以下文章