SpringBoot3 踩坑实录
SpringBoot3 踩坑实录
Idea
Lombok 没有正确注入
在 设置->构建、执行、部署->编译器->注解处理器 检查是否正确设置处理器路径,一般选择 从项目类路径获取处理器
即可

[!NOTE]
Idea似乎会在某种条件下自动更改到下面的
处理器路径
,而下面的路径存在问题导致出错
MapStruct & Lombok
核心冲突场景:当 Lombok 的@Data
与 MapStruct 联用时,可能出现以下错误:
[ERROR] No property named "id" exists in source parameter(s). Did you mean "null"?
根本原因是:
- 字段可见性问题:Lombok 生成的 getter/setter 默认使用
public
,MapStruct 可能无法正确识别; - 构造方法缺失:默认无参构造函数导致对象实例化失败;
- 注解处理器顺序:Lombok 注解处理器可能在 MapStruct 之前执行。
典型错误场景:
@Data
public class UserDO {
private Long id;
private String name;
}
@Mapper
public interface UserMapper {
UserDTO toDTO(UserDO userDO);
}
解决方案:
额外引入 lombok-mapstruct-binding
,使得 lombok 生成 getter、setter 方法完成后,再进行 MapStruct 生成
<!-- 1. 依赖声明 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>xxx</version>
<scope>provided</scope> <!-- 编译阶段处理后不会打包进最终产物 -->
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>xxx</version> <!-- 当前稳定版本 -->
</dependency>
<!-- 2. 处理器链配置 -->
<annotationProcessorPaths>
<!-- Lombok处理器 -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>xxx</version>
</path>
<!-- MapStruct代码生成器 -->
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>xxx</version>
</path>
<!-- 关键桥梁:让两个处理器能协同工作 -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
pom.xml 修改后依赖没有成功更新
进行了 maven clean; maven install; 清除idea缓存 等操作均无效
解决方案:在 pom.xml
右键 Maven->重新加载项目
实现依赖刷新

[!NOTE]
在进行该操作后/在修改pom.xml文件出现的右上角图标刷新后,可能出现[Getter/Setter方法找不到的问题](###Lombok 没有正确注入)
Mybatis / MyBatis Plus
配置的Json List类型字段读取总为null
在所有配置都正确的情况下调试了半天,最终发现问题出在没有配置注解 @TableName(autoResultMap = true)
,如下:
autoResultMap = false (默认),MP 不会主动生成 resultmap

autoResultMap = true


[!NOTE]
- Property = Java属性名
- Column = 数据库字段名
- JavaType = Java类型
- TypeHandler = 处理 Property 和 Column 之间实际映射关系的类
当 autoResultMap 为 true 时,MP 会自动生成 resultMap 并显示指定所有字段的 typeHandler,mybatis 才能实现正确映射;
基本数据类型及其包装类 mybatis 进行了内置,因此不显示指定也可以正常映射,而其他类型则需要显示指定,mybatis 才能正确映射。
解释:
resultMap 是 MyBatis 中定义 数据库结果集与 Java 对象映射关系的核心配置,用于解决以下问题:
- 字段名与属性名不一致(如数据库 user_id 映射到 Java userId)
- 复杂类型嵌套(如映射集合 List、关联对象 User)
- 构造函数注入(通过
<constructor>
调用特定构造方法)
MyBatis Plus 对其进行了增强:
@TableName(autoResultMap = true)
是 MyBatis-Plus 框架中用于注解实体类的一个重要特性。这里的autoResultMap = true
对于确保某些高级映射功能(特别是类型处理器TypeHandler)能正确工作至关重要。(正中下怀,狗屎!!!!!找了一天了)如果在实体类的字段上使用了自定义的类型处理器(例如,通过
@TableField(typeHandler = XxxTypeHandler.class)
注解,或者全局注册的类型处理器),那么必须设置autoResultMap = true
。只有这样,MyBatis-Plus 在执行查询(包括它内置的
selectById
,selectList
等方法)并将结果集映射到实体对象时,才会正确地调用指定的类型处理器来转换数据。对于正常的流程来说,我们需要自己在 mapper.xml 编写相关 sql 并指定 resultMap,在 mapper 类中定义接口才能使用;
对于 MP 来说,相当于为我们预定义或实现了通用的 CURD,而不需要在 xml 中进行编写 sql、在 mapper 类中声明接口,但由于数据类型映射的复杂性加上范型擦除等 Java 特性,使得 MP 无法准确完成映射,因此需要我们手动指定明确类型并配置 resultMap。
问题1: 是否可以通过手动设置 ResultMap 实现非常规类型字段映射的目的(如Json List类型的自动映射)?
在MyBatis的核心用法中,ResultMap
是实现复杂映射(包括使用类型处理器)的标准方式。您可以:
在 Mapper XML 文件中定义
ResultMap
: 在对应的Mapper XML文件中,可以手动定义一个ResultMap
,并在其中为需要特殊处理的字段指定typeHandler
,如示例中的tags
:<mapper namespace="online.yzketx.matematch.mapper.UserMapper"> <resultMap id="userWithTagsResultMap" type="online.yzketx.matematch.model.domain.User"> <id column="user_id" property="userId"/> <result column="username" property="username"/> <result column="tags" property="tags" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/> </resultMap> <select id="selectUserByIdWithTags" resultMap="userWithTagsResultMap" parameterType="long"> SELECT user_id, username, tags FROM user WHERE user_id = #{userId} </select> </mapper>
在Mapper接口中定义对应的方法:
public interface UserMapper extends BaseMapper<User> { User selectUserByIdWithTags(@Param("userId") Long userId); }
当调用
userMapper.selectUserByIdWithTags(1L)
时,MyBatis会使用定义的userWithTagsResultMap
,此时JacksonTypeHandler
就会被应用于tags
字段。手动配置
ResultMap
的影响范围:- 如果通过XML手动配置了
ResultMap
,那么只有那些在<select>
(或其他SQL语句标签) 中明确指定了resultMap="yourResultMapId"
的查询才会使用这个手动配置的ResultMap
。 - 对于MyBatis-Plus提供的通用CRUD方法(如
selectById()
,selectList()
,page()
等),它们默认情况下不会自动查找并使用在 XML 中定义的任意名称的ResultMap
。
- 如果通过XML手动配置了
autoResultMap = true
的优势在于:
它告诉MyBatis-Plus为该实体自动生成一个默认的、支持高级特性(如注解在字段上的TypeHandler)的 ResultMap
,并且这个自动生成的 ResultMap
会被MyBatis-Plus的所有内置通用方法所使用。这样,就不必为每个查询都去XML里写一遍 ResultMap
或者担心通用方法不支持TypeHandler注解了。
问题2:既然自动转换 (autoResultMap = true
) 这么有效,为什么默认为 false
?
这是一个关于框架设计取舍的好问题。autoResultMap
默认为 false
可能有以下几方面原因:
- 保持与MyBatis核心行为的一致性与简单性:
- 在核心MyBatis中,如果您不显式定义
ResultMap
,MyBatis会采用最基本的自动映射策略(基于列名和属性名匹配)。MyBatis-Plus作为其增强工具,默认行为上可能倾向于保持这种基础的一致性。 - 对于简单的实体和CRUD操作,基本的自动映射已经足够,不需要生成一个更复杂的
ResultMap
。
- 在核心MyBatis中,如果您不显式定义
- 性能考虑 (尽管在现代应用中影响通常微乎其微):
- 动态地为每个实体都生成一个完整的、包含所有字段映射(包括潜在的类型处理器、关联等)的
ResultMap
对象,并在应用启动或首次使用时进行缓存,理论上会比最简单的直接映射增加一点点开销。 - 虽然这点开销对于大多数应用来说可以忽略不计,但框架设计者在设定默认值时,有时会倾向于选择性能开销最小、最轻量的选项。
- 动态地为每个实体都生成一个完整的、包含所有字段映射(包括潜在的类型处理器、关联等)的
- 显式优于隐式 (Principle of Least Astonishment):
- 高级特性(如自动应用字段上的TypeHandler注解到通用方法中)需要更复杂的内部机制(即自动生成ResultMap)。将这种行为设置为需要用户显式开启 (
autoResultMap = true
),可以让用户更清楚地知道自己启用了什么,避免了一些“魔法”行为可能带来的困惑。 - 用户只有在确实需要这些高级映射特性时(比如使用了TypeHandler),才去开启它。
- 高级特性(如自动应用字段上的TypeHandler注解到通用方法中)需要更复杂的内部机制(即自动生成ResultMap)。将这种行为设置为需要用户显式开启 (
- 避免不必要的复杂性:
- 如果一个实体非常简单,没有任何特殊的类型处理或关联,那么自动生成一个完整的
ResultMap
可能是非必要的。默认false
可以避免为简单情况引入不必要的内部复杂性。
- 如果一个实体非常简单,没有任何特殊的类型处理或关联,那么自动生成一个完整的
Knife4j
在线接口文档泛型不显示问题
参阅官方文档的解释 Swagger字段属性说明不显示
- 针对包含泛型的返回值不去使用注解说明返回值
- 注解类型的@Schame不可添加example字段
[!TIP]
能让框架获取的尽量让框架自己获取
在线接口文档 /doc.html 页空白问题
在 SpringBoot 项目中,若进行了如下配置,会导致上述问题:
spring:
web:
resources:
add-mappings: false
# 对于纯后端项目 / nginx 路由冲突问题,禁用 spring 对静态资源的扫描
解决方案:
继承 WebMvcConfigurer 接口,对 knife4j 相关静态资源进行放行:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* @param registry 资源映射注册器
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 解决设置 spring.web.resources.add-mappings=false 后,knife4j 无法访问的问题
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
引入 Knife4j 后,项目报错:java.lang.NoSuchMethodError
主要是 Spring 3.4+ 更新了 controllerAdvice 的构造方法,导致 openapi 和 controllerAdvice 的冲突,springdoc-openapi v2.7.0 在 Nov 24, 2024 宣布支持 Springboot 3.4.0
更新日志:https://github.com/springdoc/springdoc-openapi/releases/tag/v2.7.0
解决方案:
若一定要使用 SpringBoot 3.4+
在 pom.xml 中如下使用即可
<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> <version>4.5.0</version> </dependency> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.7.0+</version> </dependency>
但是要关闭增强功能
knife4j: enable: false
暂时降级至 SpringBoot 3.4-
更多解决详见 Github Issue:Spring Boot 3.4.0 引入knife4j-openapi3-jakarta-spring-boot-starter报错