SpringBoot 3.4.x踩坑记录及解决方案(持续更新)
大概2024年底SpringBoot 3.4.x正式发布之后,我陆续把手上的几个服务从2.7/3.2往3.4.x迁。本来以为就是改个版本号、跑一遍测试、修修废弃API的事,真正动起手来才发现,这个版本踩坑点相当密集。3.4.x基于Spring Framework 6.2,强制JDK 17起步,底层已经全面切到Jakarta EE 10规范,很多第三方依赖和旧代码会在编译期、启动期、运行期依次爆雷。这篇文章就把我实际遇到的和群里朋友分享的高频问题整理出来,尽量给出根因和可直接落地的方案,我会持续更新,遇到新的坑就补进来。
1. 升级3.4.x前必查:版本基线、依赖冲突和构建插件
1.1 为什么升级完项目连启动都启动不了:JDK与Jakarta基线
最先要确认的事情不是代码,而是环境和依赖基线。SpringBoot 3.4.x要求JDK 17及以上,如果你还在JDK 8环境下用IDE直接跑,通常会在项目加载阶段就报“class file version 61.0”这类错误,意思是编译出的字节码版本是Java 17,而当前JVM只支持到52.0(JDK 8)。所以第一步是把本机JDK、Maven/ Gradle的JAVA_HOME、IDE的Project SDK统一换成17或21。我在实际项目里用的是JDK 21,跑3.4.1和3.4.2都没问题。
第二个隐蔽的坑是javax.*到jakarta.*的迁移。SpringBoot 3.x整体切换到Jakarta命名空间,但很多人从2.7升级时只改了starters,没改业务代码里的import。比如javax.servlet.http.HttpServletRequest要改成jakarta.servlet.http.HttpServletRequest,javax.persistence.*要改成jakarta.persistence.*。如果你项目里有老的自定义starter或本地jar包,它们内部还在用javax,那就会在编译期直接报“程序包javax.servlet不存在”。这块没有捷径,只能全局搜索替换,加上依赖树检查。
还有一点容易被忽略:SpringBoot 3.4.x里部分内置组件和自动配置的实现也变了,比如Actuator端点的暴露方式、Jackson的默认配置、spring.factories自动装配文件的加载方式,都跟2.x有差异。升级前建议拉一份官方的spring-boot-3.4-upgrade-notes看一遍,别等报错再猜。
1.2 依赖树排查与Spring Boot Maven Plugin的编译坑
版本冲突是升级后最烦人的问题之一。SpringBoot 3.4.x通过spring-boot-dependenciesBOM统一管理了大量第三方依赖的版本,比如Jackson 2.18、Netty 4.1、SnakeYAML 2.2等。如果你项目里手动指定了某个旧版本,或者某个第三方SDK传递依赖把版本拉低了,就会出现各种奇怪的问题。比如我遇到过Jackson版本被旧工具包拉到2.15之后,ObjectMapper在处理Java 17的record时报错。解决方法很直接:
mvn dependency:tree -Dverbose -Dincludes=com.fasterxml.jackson看输出里哪些依赖把Jackson版本改了,然后通过<exclusion>排除,或者在自己项目的<dependencyManagement>里显式锁定版本。SpringBoot 3.4.x的BOM已经经过整体测试,尽量不要自己再改核心依赖的版本,除非你确实知道要解决什么问题。
再来说Spring Boot Maven Plugin。3.4.x的spring-boot-maven-plugin在repackage目标上默认把应用打成可执行jar,这个机制本身没问题,但有几个点容易踩。一个是如果你在pom.xml里漏了<mainClass>配置,而项目里有多个main方法,打包会报“Unable to find main class”或者打出来的jar启动时找不到入口。另一个是如果你同时用了maven-shade-plugin或assembly-plugin,会跟repackage冲突,造成启动时No main manifest attribute。我的建议是:SpringBoot项目只保留spring-boot-maven-plugin,自定义的构建插件尽量别跟它混用。
还有一个3.4.x新增的细节:插件默认会生成一个带有版本信息的三层jar结构(BOOT-INF/lib等),这导致你在容器里用java -cp去加载业务类时会找不到路径。如果你有那种“在运行期动态编译业务类”的需求,要改用PropertiesLauncher并设置loader.path,或者直接依赖外置classpath。
2. 配置文件、自动装配和容器初始化中的坑
2.1 IDEA里application.yml不提示?多半不是IDE的锅
很多人在IDEA里打开SpringBoot项目的application.yml,发现写spring.datasource.url时没有代码提示,或者配置项标灰。之前我以为是IDEA抽风,后来发现根因是项目里缺少spring-boot-configuration-processor。SpringBoot的配置提示机制,是在编译期扫描@ConfigurationProperties注解的类,生成META-INF/spring-configuration-metadata.json,IDEA再根据这个元数据文件做提示。如果你没引入这个处理器,那IDEA只能靠猜测,自然不提示。
解决方案是引入依赖并在maven-compiler-plugin里加上annotationProcessorPaths:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>注意编译时如果IDE显示Unknown property之类的警告,重新import Maven项目,然后mvn clean compile一次,让元数据文件生成出来,再重启IDEA。如果还是不提示,检查一下IDEA的Spring插件有没有启用,File -> Settings -> Plugins,确保Spring Boot插件没被禁用。
至于yml文件本身,缩进问题在3.4.x里依然很经典。用Tab缩进、或者数组元素没对齐,都会导致启动时YamlException或者属性解析成null。我见过最诡异的问题是server.port写在application.yml里生效,但用spring.config.import引入的外部配置不生效,结果发现是文件名写成了application-dev.YAML,SpringBoot默认只认application-{profile}.yml和application-{profile}.yaml,扩展名大小写也讲究。别问,问就是吃过亏。
2.2 自动装配机制从spring.factories换成AutoConfiguration.imports
SpringBoot 3.4.x已经完全移除了对spring.factories里EnableAutoConfiguration配置项的加载支持。如果你维护过自定义starter,还在用老方式:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.MyAutoConfiguration那么升级后这个starter会静默失效,也就是你的@ConditionalOnClass、@Bean都不会执行,服务启动也不报错,但功能就是没生效。正确做法是在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件里,一行一个自动配置类全限定名。
我建议把AutoConfiguration.imports当作一个独立的资源文件,放在src/main/resources/META-INF/spring/下。同时,如果你的自动配置类里有条件注解,记得按顺序写清楚。比如@AutoConfiguration可以配合@AutoConfigureBefore、@AutoConfigureAfter来调整加载顺序,避免出现依赖的Bean还没注册就执行了初始化逻辑。
跟自动装配相关的高频面试题“SpringBoot自动装配原理是什么”,在3.4.x里答案也更新了。核心就三步:@SpringBootApplication中引入了@EnableAutoConfiguration,然后通过AutoConfigurationImportSelector加载AutoConfiguration.imports里的配置类,最后配合@Conditional系列注解按条件装配。有些文章还在讲spring.factories,那已经是老黄历了,表达出来面试官反而会觉得你版本知识更新不及时。
2.3 循环依赖和事务失效,3.4.x下最容易翻车的两个点
循环依赖在SpringBoot 3.x里被限制得更严格。Spring Framework 6.x默认不再支持构造器注入的循环依赖,如果你以前靠@Lazy、@Autowired字段注入来掩盖设计问题,升级3.4.x后启动阶段会直接报The dependencies of some of the beans in the application context form a cycle。这里我的建议分两层:
- 短期:把相互依赖的Bean改成
@Lazy延迟注入,或者抽一个中间层。 - 长期:用构造器注入+按业务域拆分,把Service层的循环依赖从根上消掉。
我在实际项目里就有过A依赖B、B依赖A的案例,解决方案是把公共的逻辑抽到C中,A和B都依赖C,问题自然消失。
事务失效的问题在3.4.x里没有新增太多机制,但常见场景依旧高发。最典型的是同一个类内部方法之间调用this.doSomething(),外层方法没有@Transactional,内层方法即使加了注解也不会进入代理逻辑。这在面试里经常考,但实际开发还是很多人踩。解决方式是通过@Autowired注入自身代理,或者把事务方法放到另一个Service里。另一个场景是@Transactional加在private方法上,注解直接不生效,因为Spring默认用CGLIB代理,private方法不可代理。还有一个冷门但真实的坑:多数据源场景下,@Transactional默认使用主事务管理器,如果你的事务注解没指定transactionManager,它会往Primary数据源上提交,造成“明明操作了两个库,但一个提交成功一个回滚不了”。这个在3.4.x下还是要用@Transactional(transactionManager = "xxxTransactionManager")显式指定。
3. 数据访问与中间件集成:一套组合拳下来能踩到一半的坑
3.1 老项目JDK 8打包到Docker Desktop:别只改镜像基础层
“SpringBoot JDK 1.8打包到Docker Desktop”这个场景我太有感触了。很多老项目业务代码还是JDK 8风格,但SpringBoot 3.4.x不支持JDK 8,所以你必须先解决JDK版本问题,而不是只换Docker基础镜像。我的实操路线是:
- 项目先升到JDK 17,代码层面主要改javax到jakarta,以及个别API调整。
- 确认本地
mvn package能出包。 - Dockerfile基础镜像采用
eclipse-temurin:17-jre-alpine或者更小的17-jre-slim,别用已经没人维护的老版本。 - 启动命令用
ENTRYPOINT ["java","-jar","/app/app.jar"]。
如果Docker Desktop在Mac/Windows上运行慢,多半是文件共享和内存限制问题。可以在Docker Desktop的Settings -> Resources里把内存调到4GB以上,把项目目录加入File Sharing列表。另外,构建镜像时尽量利用Maven的缓存,避免把~/.m2整个塞进镜像。我推荐用多阶段构建:
FROM maven:3.9-eclipse-temurin-17 AS build WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn clean package -DskipTests FROM eclipse-temurin:17-jre-alpine WORKDIR /app COPY --from=build /app/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java","-jar","/app/app.jar"]这里有个小技巧:dependency:go-offline在依赖没变更时会复用缓存,构建速度快很多。
3.2 MyBatis、Oracle、Redis、MinIO、ActiveMQ、Quartz逐个过
每天崩一次,不如一次全崩一遍,下面这几个中间件我放一起说。
MyBatis集成:eclipse里“springboot集成mybatis一直报错downloading...”这个问题,多半是IDE在后台下载MyBatis相关依赖时访问Maven中央仓库超时。解决方式是把仓库镜像换成阿里云或华为云,在settings.xml里加mirror配置,不要只改项目里的<repositories>。其次是版本适配,SpringBoot 3.4.x建议用mybatis-spring-boot-starter3.0.3+,老版本2.x在Jakarta命名空间下会直接编译不过。
Oracle数据库:JDK 17环境下要用ojdbc11或更新的驱动,不能再依赖老的ojdbc8(虽然有时候能跑,但会有模块访问报错)。Maven坐标是com.oracle.database.jdbc:ojdbc11,注意Oracle官方驱动没有开放到中央仓库时,需要你自己装到本地仓库或使用公司私服。连接配置上,SpringBoot 3.4.x的spring.datasource配置基本没变,但建议把driver-class-name写成oracle.jdbc.OracleDriver,避免版本兼容问题。
Redis:SpringBoot 3.4.x里使用Redis推荐走spring-boot-starter-data-redis,默认客户端是Lettuce,连接池默认不启用。如果高并发下出现RedisConnectionFailureException: Unable to connect to Redis,除了网络问题外,还要检查spring.data.redis.lettuce.pool.max-active和max-idle是否配置。很多人升级后忘了在application.yml里加连接池配置,结果数据库连接打满直接卡死。示例:
spring: data: redis: host: localhost port: 6379 lettuce: pool: max-active: 16 max-idle: 8 min-idle: 2MinIO:SpringBoot整合MinIO时,用io.minio:minio的8.5.x版本比较稳。坑主要在endpoint地址,http://localhost:9000和http://localhost:9000/有区别,有些版本对结尾斜杠敏感。另外,上传大文件时要合理设置partSize,或者直接分片上传,否则内部会用multipart临时文件,磁盘空间不够就会报InsufficientDataException。
ActiveMQ:SpringBoot 3.x里spring-boot-starter-activemq名字没变,但需要注意jakarta.jms包。如果项目里直接import javax.jms.*,编译能过但运行时会报ClassNotFoundException。还有一点,ActiveMQ的spring.activemq.pool.enabled默认是false,如果你要做连接池,需要额外引入activemq-pool依赖并设置pool.enabled=true。
Quartz:SpringBoot 3.4.x整合Quartz时,很多人会纠结要不要引入spring-boot-starter-quartz。其实这个starter是Spring官方把Quartz整合进Spring管理的封装。坑在于:如果你只用Quartz原生的SchedulerFactoryBean,又要同时用Spring的@Autowired注入Service,那Job类里会出现null注入。正确做法是让Job继承QuartzJobBean,然后在QuartzConfig里通过JobDetailFactoryBean把JobDataMap传进去,或者用SpringBeanJobFactory让Job可注入。我实测org.springframework.boot:spring-boot-starter-quartz:3.4.x是可用的,就是注意Job类不要手动new,必须交给Spring管理。
3.3 工作流与三方接入:Flowable、PowerJob、WebSocket、HanLP
Flowable整合SpringBoot在3.4.x下也有兼容性问题。Flowable本身的版本要选择与SpringBoot 3.x兼容的,比如Flowable 7.0.0之后的版本。如果项目里出现flowable-spring-boot-starter自动配置不生效,先确认引入的版本号,再检查processEngine是否被重复定义。一种常见的现象是:项目里自己定义了一个ProcessEngine的Bean,覆盖了Flowable的自动配置,导致默认的流程图部署、历史数据表都不生成。这种情况建议只保留Starter官方配置,除非你有非常强的定制需求,否则不要重复定义核心Bean。
PowerJob集成时,最典型的坑是powerjob-client的版本与SpringBoot 3.4.x的兼容性。旧版客户端里会用到javax.annotation.Resource,JDK17下编译不报错但运行时可能产生注解扫描异常。可以引入最新版powerjob-client,并在启动类上排除自动配置扫描包,把Worker的初始化逻辑放到@PostConstruct里做。只要保证PowerJobWorker在应用完全启动后再初始化,通常不会有大问题。
第三方WebSocket调用这块,我踩过的是“连接第三方推送服务时握手总是失败”。排查下来发现是SpringBoot默认的WebSocketClient没有设置Origin请求头,对方服务做了跨域校验。解决方法是自定义StandardWebSocketClient,在握手阶段添加Origin头。代码不复杂:
WebSocketClient client = new StandardWebSocketClient() { @Override protected void customizeRequest(WebSocketHttpHeaders headers) { headers.setOrigin("https://yourdomain.com"); } };还有一种情况是第三方服务需要带Token握手,一定要在WebSocketHttpHeaders里显式设置Authorization头,否则收到403。另外,断线重连机制一定要自己实现:监听onClose后延时重连,不要用死循环硬怼,不然会被对端封IP。
HanLP分词在SpringBoot里集成,问题大多出在原生库加载。HanLP的hanlp-lucene-plugin、hanlp-portable等构件之间版本混杂,容易造成NoClassDefFoundError。建议只保留一个核心依赖,并且把HanLP的data路径通过配置项指定到可写目录,不要放在jar内部。如果是SpringBoot 3.4.x + JDK17,注意模块系统对--add-opens的要求,启动参数里加:
--add-opens java.base/java.lang=ALL-UNNAMED否则容易在分词器初始化时反射报错。这个问题我在Mac和Linux上都遇到过,加了这个参数之后稳定很多。
4. 安全认证、文件处理与前后端对接的实战细节
4.1 已有Token验证还想用API Key安全对接,怎么绕开?
很多项目已经有基于Spring Security或JWT的Token认证机制,现在外部系统说要走API Key对接,又不想破坏现有登录体系,怎么办?这里是“安全对接”的核心:不要把API Key和用户Token混为一套,让API Key作为独立的认证通道,只对特定URL生效。我在项目里用的方案是自定义过滤器:
@Component public class ApiKeyAuthFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String apiKey = request.getHeader("X-API-KEY"); if (checkApiKey(apiKey)) { chain.doFilter(request, response); } else { response.setStatus(401); response.getWriter().write("invalid api key"); } } }但直接全局注册这个过滤器会拦截所有请求,体验很不好。更好的方式是:只对/api/external/**或/openapi/**路径生效,其它路径继续走原来的Token体系。实现方式有两种:
- 在过滤器里判断
request.getRequestURI()是否匹配白名单前缀。 - 或者用
SecurityFilterChain里的requestMatchers指定不同认证规则。
如果项目没用Spring Security,只是普通拦截器,也可以用HandlerInterceptor+WebMvcConfigurer.addInterceptors把API Key校验注册到特定pathPattern上,效果一样。记得API Key不要放在URL参数里,容易打进日志,统一放Header,并且服务端对比时要用MessageDigest.isEqual做常量时间比较,防止时序侧信道攻击。
4.2 静态资源映射、微信验证文件与大文件上传下载
SpringBoot 3.4.x的静态资源默认路径是classpath:/static/,大部分情况够用。但如果项目配置了虚拟路径映射,会涉及到WebMvcConfigurer的addResourceHandlers:
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/files/**") .addResourceLocations("file:" + uploadDir + "/"); }这里有个坑:addResourceLocations的路径结尾必须带/,否则映射不生效。如果你想支持HTTP范围请求(比如视频拖动进度条),SpringBoot 3.4.x默认支持一部分,但如果你自己实现了资源处理的Controller,很可能把Range头给丢了,导致前端无法拖拽播放。
“配置微信域名文件认证”这个场景在前后端分离项目里很常见。微信公众平台要求把校验文件放在域名根目录下,很多人以为必须放在Nginx根目录,其实SpringBoot里可以通过资源映射直接返回这个文件内容。把验证文件放到resources/static/下,或者通过ResourceHttpRequestHandler映射到指定路径,只要保证访问/MP_verify_xxx.txt能返回文件内容即可。注意响应头不要被全局拦截器改成application/octet-stream,微信那边校验的是文本内容,text/plain或者无Content-Type都行。
大文件上传下载这块,优先在application.yml里调整:
spring: servlet: multipart: max-file-size: 1024MB max-request-size: 1024MB但光调配置还不够。上传大文件时,如果用内存处理,很容易OOM。建议直接把MultipartFile转存到磁盘临时目录,再异步做后续处理。下载大文件时,用InputStreamResource配合ResponseEntity流式输出,避免把整个文件读进byte[]。我写过一个稳定方案:
@GetMapping("/download/{fileName}") public ResponseEntity<Resource> download(@PathVariable String fileName) throws IOException { Path file = Paths.get(uploadDir).resolve(fileName); InputStreamResource resource = new InputStreamResource(Files.newInputStream(file)); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")) .contentType(MediaType.APPLICATION_OCTET_STREAM) .contentLength(Files.size(file)) .body(resource); }contentLength一定要有,否则前端无法显示进度条。大文件最好再配合分片/断点续传,但这属于另一个话题了,这里不展开。
4.3 单元测试与Vue前后端分离联调的正确姿势
SpringBoot 3.4.x的单元测试最佳实践,我是从踩坑里总结出来的。首先,@SpringBootTest在3.4.x下默认会加载完整应用上下文,启动慢。建议能切片就切片,比如Controller层用@WebMvcTest,只加载Spring MVC相关配置,Service层用@ExtendWith(MockitoExtension.class)做纯Mock测试,数据访问层用@MybatisTest或@DataJpaTest。这样整体测试时间能减少70%以上。
其次,@SpringBootTest里如果只想用某个profile的配置,一定要加@ActiveProfiles("test"),否则默认的application.yml可能连数据库和Redis地址都指向生产环境。之前同事写过一次,测试中把生产库表给清了,幸好是测试环境。
谈到Vue前后端分离,最核心的问题是跨域。开发环境推荐用Vue CLI的proxy配置,让前端请求转发到后端,浏览器认为同源,避免CORS:
// vue.config.js module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }生产环境则用Nginx反向代理,SpringBoot本身尽量不要开cors.allow-origin=*,因为这样会有安全风险。如果一定要在后端配置CORS,用WebMvcConfigurer里的addCorsMappings,把allowedOrigins限定到具体域名,而不是*。还有一个细节:请求带了自定义Header(如Authorization),CORS预检OPTIONS请求也会带上对应Header,后端需要允许对应allowedHeaders,否则浏览器会拦截真实请求。这个问题在前后端联调时特别隐蔽,接口在Postman里好好的,浏览器就是不行,多半是预检没过。
5. 高频报错排查速查表与开发效率工具
5.1 IDE、构建工具的疑难杂症
IDEA里application.yml不提示的问题前面已经说了,主要是缺spring-boot-configuration-processor。还有一类情况是IDEA的Spring Boot Run Configuration里没有识别到项目,右键运行主类时只能显示java不能显示Spring Boot App,这时检查IDEA里有没有引入Spring插件,并将项目标记为SpringBoot项目(右键项目 -> Add Framework Support)。
VSCode启动SpringBoot项目,如果手头没有IDEA License,完全可以用VSCode。需要安装Extension Pack for Java、Spring Boot Extension Pack,然后在.vscode/launch.json里配置:
{ "type": "java", "name": "debug (SpringBoot)", "request": "launch", "mainClass": "com.example.Application", "projectName": "your-project" }或者直接用终端mvn spring-boot:run跑起来也一样。VSCode的坑在于自动增量编译不如IDEA及时,改动Java代码后需要手动Build,不然调试时走的是旧class。
Eclipse里遇到的“MyBatis下载依赖一直卡住”,本质是Maven仓库访问慢。除了改镜像,还有一个操作:在Eclipse的Window -> Preferences -> Maven -> User Settings里,确认settings.xml路径正确,并在pom.xml里给spring-boot-maven-plugin和mybatis-generator-plugin配上镜像仓库。Eclipse自带的Maven版本有时比较旧,建议选择外部的Maven 3.9.x。实测下来,同样的项目在Eclipse里比IDEA更容易出现缓存冲突,遇到诡异问题先mvn clean再来一次。
5.2 面试热点与规范沉淀:自动装配原理、事务失效、开发规范
SpringBoot 3.4.x相关的面试题,很多还是围绕老知识点变着法问,但答案要更新。比如“自动装配原理”,3.4.x下要提到AutoConfiguration.imports、@Conditional、@AutoConfiguration注解,并且能现场画一下启动流程。再比如“事务失效场景”,除了自调用、private方法、异常被吞掉之外,还要知道SpringBoot多数据源下事务管理器的选择问题,这是3.x版本带来的新层次。
开发规范这块,我最近在团队里用了一套可复用的个人checklist:新建模块时必须定义starter依赖版本管理、配置项集中到application.yml按环境拆分、所有外部访问走接口文档化、新加中间件必写单元测试和启动自检。大家常说的SpringBoot开发规范,很多不是技术问题,而是工程纪律。比如热词里提到的“Claude Skill”,说白了就是让AI在代码生成时遵守团队规范的提示词模板,把约定沉淀成可以复用的skill,能减少不少Review返工。
关于banner生成器这类的“偏门热词”,我也说一句:SpringBoot启动时的banner虽然不影响功能,但团队内部环境区分很有用。可以让测试环境打印红色警告“TEST ENV”,生产环境打印“PROD ENV”,这样别人看日志截图时不会搞混环境。实现方式是src/main/resources/banner.txt,或者在启动类里用SpringApplication.setBanner编程式设置。如果想在线生成艺术字,可以用一些banner生成器,然后复制到banner.txt里,也算一种团队仪式感。
5.3 本地到测试环境部署:Docker、配置加密和数据库连接
最后把本地到测试环境部署的常见坑串一遍。
Docker部署SpringBoot项目:除了前面说的多阶段构建,启动命令里建议加-Dspring.profiles.active=test和-Djava.security.egd=file:/dev/./urandom,后者能加快在容器里的随机数初始化速度。端口映射记得写-p 8080:8080。如果要看实时日志,用docker logs -f 容器名。
配置加密:数据库密码这类敏感配置,不能明文躺在application.yml里。热词里提到SM4加密结合Jasypt的方案,思路是:用Jasypt的StringEncryptor接口自定义一个加密器,内部用SM4算法加解密,然后把配置写成ENC(密文),Jasypt在启动时自动解密。需要注意Jasypt的版本要和SpringBoot 3.4.x兼容,从jasypt-spring-boot-starter的3.0.5+开始基本没问题。有一点要提醒:自定义加密器时,不要在application.yml里把密钥也放进去,否则等于没加密。密钥用环境变量或启动参数传入,例如-Djasypt.encryptor.password=${DB_PASSWORD}。
数据库连接:如果你在3.4.x里用HikariCP连接Oracle、PostgreSQL,建议配置maximum-pool-size和connection-timeout。默认的HikariCP参数比较宽松,生产环境高并发下容易拖垮数据库。我一般把核心服务配成:
spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 idle-timeout: 600000很多人遇到“连接数忽高忽低”或者“接口偶尔卡顿”,就是因为连接池参数没调。
SpringBoot 4.0找不到AOP:热词里提到SpringBoot 4.0找不到AOP,这个我还没有在正式项目上大规模使用4.0,但可以判断的是:SpringBoot 4.0会把模块边界收得更紧,spring-boot-starter-aop不能再靠传递依赖带进来,需要显式引入。所以未来升级4.0时,第一件事就是检查AOP相关依赖是否显式声明。目前在3.4.x上,@Aspect注解不生效的排查顺序是:有没有引spring-boot-starter-aop、有没有开启@EnableAspectJAutoProxy、类有没有被Spring容器管理、切点表达式是否写对。
我个人在实际操作中最深的一点体会是:SpringBoot版本升级这件事,最大的成本往往不是语法层面的修改,而是“原来能用但不知道为什么能用的东西,突然不能用了”。所以每升一个版本,我都会建议团队把spring-configuration-metadata.json、AutoConfiguration.imports、依赖树这三个东西先拉出来看一遍,心里有个底再动手。后续如果我在生产环境再遇到新的3.4.x问题,会继续更新在文章里。也欢迎你在评论区告诉我遇到过什么奇葩报错,我验证过之后会补充进来。