你盯着屏幕上那个“已启动成功”的日志心想一切正常。可当第一个请求过来接口返回的日期时间格式不对第二个请求触发了一个诡异的空指针第三个请求直接把内存撑爆了。你翻遍所有业务代码找不到任何逻辑错误——这时候大概率是某个你根本没正眼瞧过的配置文件在暗处作祟。SpringBoot号称“约定优于配置”但那份“约定”其实是一张细密的网任何一处疏漏都会让整个应用行为失常。下面这十一个配置盲区我敢说你至少遇到过其中三个。那个被你忽略的bootstrap.yml到底干了什么大多数开发者只认识application.yml却很少会在意bootstrap.yml。实际上Spring Cloud 环境下 bootstrap.yml 的优先级高于 application.yml它负责加载外部配置源比如配置中心、加密密钥。如果你把数据库密码直接写在 application.yml 里而配置中心又在 bootstrap 阶段才初始化——密码根本不会生效。更隐蔽的是bootstrap.yml 中的spring.application.name决定了后续所有配置的命名空间。改了这个名字连接 Consul、Nacos 时可能全盘失效。另一个常见陷阱bootstrap.yml 里的server.port不会被识别要改端口必须写在 application.yml 里。配置文件加载顺序你以为的“覆盖”可能根本没发生SpringBoot 加载配置的规则是一个多层塔依次从file:./config/→file:./→classpath:/config/→classpath:/读取。但你部署时如果把 jar 包和外部 config 目录放在一起优先级确实最高——问题是很多人把外部配置放在application-{profile}.yml里而 Profile 文件只在特定情况下才被读取。举个例子你在 jar 包同级目录下放了config/application-prod.yml但启动命令里只写了java -jar app.jar没加--spring.profiles.activeprod那么 prod 配置永远不生效。Profile 激活不是通过文件存在自动触发的必须显式指定。另一个冷知识PropertySource注解无法作用于 YAML 文件你如果用它指向一个 .yml整个应用会静默失败。ConfigurationProperties和Value谁比谁更坑Value用起来简单但它有两个致命弱点不支持复杂的嵌套结构比如user.addresses[0].city这种也不支持松散绑定你在配置里写user-nameValue 必须用Value(${user-name})一字不差。而ConfigurationProperties能自动把user-name映射到userName字段上。更隐蔽的是ConfigurationProperties 默认不会校验。如果你在配置里写了个字符串给server.portSpring 会静默地把它转成 int遇到非数字直接抛异常。加上Validated注解才能触发校验。还有ConfigurationProperties类的属性必须有 setter 方法否则注入失败很多人在 Kotlin 或 Lombok 环境下忘记生成 setter结果属性全为 null查半天。日志配置你还在用logging.level.rootINFO就完事了生产环境中只靠一个 INFO 级别什么也查不到。但问题是SpringBoot 默认的日志是 Logback它只在classpath:logback-spring.xml存在时才覆盖默认配置。如果你直接在 application.yml 里写logging.pattern.console...它只能影响控制台输出文件日志的模式根本不受影响。还有一个容易踩的雷日志文件的滚动策略。默认按10MB滚动但如果你的应用一天产生 20GB日志10MB 切一次会疯狂产生小文件磁盘 IO 直接打满。你应该在配置里明确指定logging.logback.rollingpolicy.max-history7和logging.logback.rollingpolicy.total-size-cap5GB。不配置日志目录可能在几天内撑爆磁盘。数据库连接池HikariCP 的默认值真的适合你吗SpringBoot 2.x 默认用 HikariCP官方说它是“最快连接池”但默认的maximum-pool-size10对于高并发业务完全是灾难。如果你的应用有 20 个线程同时访问数据库10 个连接瞬间用完剩余的请求全在排队。而排队时间默认为 30 秒connection-timeout30000用户等半分钟才看到超时错误。更隐蔽的是连接泄漏。HikariCP 默认不主动检测泄漏需要设置leak-detection-threshold2000毫秒如果一个连接被占用超过 2 秒没归还就打印警告。很多团队直到线上出现“连接不可用”错误才想起打开这个开关。另一个坑idle-timeout默认 10 分钟如果你的数据库有连接空闲超时比如 MySQL 的 wait_timeout8小时HikariCP 的max-lifetime要设置比数据库的超时短一点否则数据库干掉空闲连接后连接池还傻傻地认为连接是好的。跨域配置前端报跨域时别只怪浏览器大部分新手用CrossOrigin在 Controller 上加注解但如果你用了 Spring Security跨域配置的优先级必须高于 Security 过滤器。正确做法是注册一个CorsConfigurationSourceBean或者实现WebMvcConfigurer的addCorsMappings。否则 Security 的 CSRF 保护会先拦截 OPTIONS 预检请求直接返回 403。另外跨域配置中的allowedOrigins不能写并同时允许携带凭证allowCredentialstrue这是浏览器的安全限制。很多人在本地测试时写加上allowCredentialstrue结果 Chrome 直接报错然后跑去查前端配置。Jackson 序列化你以为JsonFormat能搞定一切时间格式是表面问题更深的是全局时间格式化配置必须在application.yml里写spring.jackson.date-formatyyyy-MM-dd HH:mm:ss但如果你同时用了JsonFormat注解注解优先级更高。问题在于很多人都不知道 Jackson 还有timezone设置默认取服务器本地时区。如果你的服务器是 UTC数据库存的是北京时间前端看到的永远是减了8小时的时间。另一个容易爆炸的点是BigDecimal 的序列化。默认情况下0.00会被输出为0.00.000输出为0.0。要保留尾随零必须配置spring.jackson.serialization.write-bigdecimal-as-plaintrue。更离谱的是Jackson 对未知字段的处理默认抛出异常如果你返回的实体类少了某个字段前端调用直接报 500。加上spring.jackson.deserialization.fail-on-unknown-propertiesfalse才能优雅忽略。异步任务配置Async 为什么没生效你给方法加了Async以为能自动异步执行结果发现还是同步阻塞。最常见的错误是调用 Async 方法的代码必须来自另一个 Bean因为 Spring 的 AOP 原理是基于代理的从同一个类内部调用代理不生效。解决办法是注入自己或者把异步方法提取到另一个 Service 里。更隐蔽的是自定义线程池。EnableAsync默认使用SimpleAsyncTaskExecutor它每次任务都新建一个线程高并发下线程数暴增直接 OOM。你需要在配置类里定义一个Executor类型的 Bean并设置corePoolSize和maxPoolSize。另一个被忽略的点是Async方法返回值必须为void或Future如果你返回普通对象Spring 会直接返回 null。健康检查为什么 /actuator/health 返回 404你在 pom.xml 里加了spring-boot-starter-actuator然后访问/actuator/health却得到 404。原因很简单从 SpringBoot 2.x 开始Actuator 默认只暴露 health 和 info 两个端点且路径是/actuator/health但很多人忘了写/actuator。另一个坑是如果你配置了server.servlet.context-path/demo那么端点路径会变成/demo/actuator/health。健康检查还有更细的配置management.endpoint.health.show-detailswhen_authorized不配置的话默认只返回 UP 或 DOWN没有详细信息。对于 Kubernetes 的存活探针和就绪探针你得分别配置liveness-state.enabledtrue和readiness-state.enabledtrue否则探针只能判断应用是否启动不能判断是否真正就绪。配置加密你把数据库密码明文写在配置文件里了吗别觉得内网就安全。任何人能接触到运维机器或代码仓库就能拿到你的所有明文密码。Spring Cloud Config 支持对称加密只需在 bootstrap.yml 里设置encrypt.key即可。但有个细节加密值必须用前缀{cipher}开头否则 Spring 不会解密。而且你要先通过/encrypt端点生成密文再粘贴到配置文件。更推荐的方案是使用Jasypt Spring Boot Starter它能在任意配置属性上解密。但Jasypt 默认使用的加密算法是 PBEWithMD5AndDES这个算法已经被认为是弱密码。你应该改成PBEWithHMACSHA512AndAES_256同时设置一个强密码。另一个容易被忽略的点解密过程发生在 Spring 初始化 Bean 之前如果你在 PostConstruct 里用 Value 获取密码但解密器还没加载完得到的还是密文。多环境配置你还在手动改 application.yml 吗很多团队靠复制粘贴多个 application-{profile}.yml 来管理环境但不同环境之间高度重复的配置比如相同的 Redis 超时时间会导致维护噩梦。正确的做法是利用Spring Boot 的 Profile 分组在spring.profiles.group里组合多个 Profile或者用spring.profiles.include引入公共配置。最容易被忽略的是--spring.profiles.active和--spring.profiles.include的区别active 会覆盖默认的 default Profile而 include 会追加。如果你在application.yml里定义了spring.profiles.includedb,cache然后启动时又设置了--spring.profiles.activeprod那么只有 prod 配置文件被加载db 和 cache 都没了。解决办法是把公共的 profile 通过spring.profiles.default设置或者用 Maven/Gradle 的 profile 在打包时就内置进去。自动配置类排除为什么你明明加了 starter 却不生效你引入了spring-boot-starter-data-redis也写了 Redis 配置但注入RedisTemplate时报错 “No qualifying bean of type”。原因很可能是其他自动配置类冲突了。SpringBoot 的SpringBootApplication注解默认会扫描EnableAutoConfiguration你可以通过exclude属性排除掉不需要的自动配置。更常见的是自定义 Starter 的自动配置不生效。SpringBoot 会扫描META-INF/spring.factories文件但你如果漏掉了org.springframework.boot.autoconfigure.EnableAutoConfiguration那一行或者写错了全限定名你的配置类就永远不会被加载。而且SpringBoot 2.7 之后推荐使用META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件旧的spring.factories方式在 3.x 里被废弃——很多人升级到 SpringBoot 3.x 后自定义自动配置全部失效就是因为没改这个文件。结尾修改一个配置的成本几乎为零但找到问题配置的时间可能是一整天。你永远不知道下一个线上事故是因为漏写了一个spring.jackson.default-property-inclusionnon_null还是因为多写了一个spring.jpa.open-in-viewtrue这个默认开启的配置会导致请求过程中数据库连接一直被持有高并发下直接拖垮连接池。建议你花两个小时把项目中所有的配置文件梳理一遍对照本文提到的每个细节逐一检查。那些被忽略的配置往往就是系统脆弱性的源头。