SpringBoot 的 “开箱即用” 让 Java 开发效率翻倍,但默认配置就是 “开发环境专属”!很多开发者直接照搬上生产,结果遭遇 OOM 崩溃、服务雪崩、用户重复登录等致命问题,连夜加班排查才发现,竟是没改几个基础配置惹的祸!
上篇分享 8 个必改配置后,无数开发者留言说 “踩中同款坑”。今天继续分享 8 个高频雷区,每个坑都附 “真实场景 + 直接复制的生产配置”,新手也能轻松上手,建议收藏转发,避免上线踩雷!
一、JVM 参数:默认配置导致 OOM 崩溃!致命坑点
SpringBoot 默认不指定 JVM 参数,堆内存仅占物理内存 1/4(16GB 服务器仅给 4GB),高并发下直接 OOM;且不开启 GC 日志和崩溃排查文件,崩了都不知道为啥!
真实场景
某电商商品服务,每周三固定崩溃,排查发现是高峰期商品数据占满堆内存,默认配置没开崩溃日志,只能靠猜,最后调整 JVM 参数才解决。
生产配置(直接复制)方式 1:启动脚本(推荐)
java -jar your-app.jar \-Xms8g \ # 16GB服务器设8GB,物理内存50%
-Xmx8g \ # 最大堆内存=初始内存,避免频繁扩容
-Xmn4g \ # 新生代占50%,提升回收效率
-XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=512m \
-XX:+HeapDumpOnOutOfMemoryError \ # OOM生成排查文件
-XX:HeapDumpPath=/var/log/oom.dump \
-XX:+PrintGCDetails \
-XX:LogFile=/var/log/gc.log
方式 2:application.yml(SpringBoot2.3+)
spring:jvm:
args: >
-Xms8g -Xmx8g -Xmn4g
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/oom.dump
-XX:+PrintGCDetails -XX:LogFile=/var/log/gc.log
关键提醒
- 堆内存按 “物理内存 ×50%~70%” 配置,32GB 服务器可设 16GB;
- 一定要开 OOM 排查文件,不然崩溃后无迹可寻!
默认用 Java 原生序列化,数据又大又乱码;无连接池,并发高了直接超时;缓存无过期时间,Redis 内存迟早撑爆!
生产配置(直接复制)第一步:引入依赖
org.springframework.boot
spring-boot-starter-data-redis
2.7.15
org.apache.commons
commons-pool2
第二步:yml 配置
spring:redis:
host: ${REDIS_HOST:127.0.0.1}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:} # 环境变量存密码,别硬编码
timeout: 3000
lettuce:
pool:
max-active: 200
max-idle: 50
min-idle: 10
cache:
type: redis
redis:
use-key-prefix: true
key-prefix: "${spring.application.name}:" # 加前缀防冲突
cache-Null-values: false # 不缓存null,防穿透
time-to-live: 3600000 # 1小时过期
第三步:序列化配置(关键)
import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JSONRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.Duration;
@Configuration
public class RedisCacheConfig {
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory factory) {
ObjectMapper objectMapper = new ObjectMapper();
GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);
StringRedisSerializer keySerializer = new StringRedisSerializer();
var config = org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1))
.serializeKeysWith(org.springframework.data.redis.serializer.RedisSerializationContext
.SerializationPair.fromSerializer(keySerializer))
.serializeValuesWith(org.springframework.data.redis.serializer.RedisSerializationContext
.SerializationPair.fromSerializer(valueSerializer))
.disableCachingNullValues();
return RedisCacheManager.builder(factory).cacheDefaults(config).build();
关键提醒
- 千万别用默认序列化,JSON 格式又小又好懂;
- 缓存必须设过期时间,避免 Redis 内存溢出!
开发环境靠前端代理掩盖跨域问题,生产环境没配置,前端所有接口报错 “跨域拦截”,用户直接用不了!
生产配置(直接复制)
import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// 生产环境指定具体域名,别用*(不安全)
config.addAllowedOrigin("https://admin.xxx.com");
config.addAllowedOrigin("https://www.xxx.com");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.setAllowCredentials(true);
config.setMaxAge(1800L); // 预检缓存30分钟
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
关键提醒
- 禁用config.addAllowedOrigin("*"),存在安全风险;
- 多环境可把域名写进配置文件,避免硬编码!
默认 Session 存服务器内存,多实例部署时,用户切换实例就需重新登录,体验极差,投诉不断!
生产配置(直接复制)第一步:引入依赖
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.session
spring-session-data-redis
第二步:yml 配置
spring:session:
store-type: redis # Session存Redis,多实例共享
redis:
namespace: spring:session:${spring.application.name}
cleanup-cron: 0 0 * * * * # 每小时清理过期Session
timeout: 1800 # 30分钟过期
redis:
host: ${REDIS_HOST:127.0.0.1}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
关键提醒
- 分布式部署必配!不然用户会频繁登录,直接流失;
- Redis 设密码,防止 Session 数据泄露!
默认定时任务用单线程,一个任务执行 10 分钟,后面的任务全阻塞,比如 5 分钟同步一次的订单,变成 1 小时才同步!
生产配置(直接复制)
import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(15); // 线程池大小,按任务数调整
scheduler.setThreadNamePrefix("scheduled-task-");
scheduler.setQueueCapacity(100);
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 捕获异常,避免线程死亡
scheduler.setErrorHandler(t -> System.err.println("定时任务失败:" + t.getMessage()));
scheduler.initialize();
return scheduler;
@Override
public void configureTasks(ScheduledTaskRegistrar registrar) {
registrar.setScheduler(taskScheduler());
关键提醒
- 线程池大小按任务数 1.5 倍设置,不用太大;
- 必须加异常处理,不然一个任务报错,整个线程池崩了!
默认 404 返回空白页,500 返回错误栈,不仅前端不好处理,还泄露接口路径、数据库表名,有安全风险!
生产配置(直接复制)第一步:统一返回格式
import lombok.Data;import java.io.Serializable;
@Data
public class Result implements Serializable {
private int code; // 200成功,其他失败
private String msg;
private T data;
public static Result success() {
return new Result<>(200, "操作成功", null);
public static Result success(T data) {
return new Result<>(200, "操作成功", data);
public static Result fail(int code, String msg) {
return new Result<>(code, msg, null);
第二步:全局异常处理器
import lombok.extern.slf4j.Slf4j;import org.springframework.web.bind.MethodArgumentNotValidexception;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
// 404异常
@ExceptionHandler(NoHandlerFoundException.class)
public Result handle404(NoHandlerFoundException e) {
log.error("接口不存在:", e);
return Result.fail(404, "请求的接口不存在");
// 参数校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handleParamError(MethodArgumentNotValidException e) {
StringBuilder errorMsg = new StringBuilder("参数错误:");
e.getBindingResult().getFieldErrors().forEach(fieldError -> {
errorMsg.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(",");
String msg = errorMsg.substring(0, errorMsg.length() - 1);
log.error(msg);
return Result.fail(400, msg);
// 兜底异常
@ExceptionHandler(Exception.class)
public Result handleOtherError(Exception e) {
log.error("系统异常:", e);
return Result.fail(500, "系统繁忙,请稍后再试");
第三步:开启 404 捕获
spring:mvc:
throw-exception-if-no-handler-found: true
web:
resources:
add-mappings: false
关键提醒
- 错误提示要友好,别暴露技术术语;
- 详细错误栈记日志,方便排查!
默认连接超时 30 秒,数据库故障时,大量请求阻塞,线程池被占满,引发全链路雪崩,数据库恢复了服务也得重启!
生产配置(直接复制)第一步:连接超时优化
spring:datasource:
hikari:
connection-timeout: 5000 # 5秒超时,快速失败
maximum-pool-size: 50
minimum-idle: 10
第二步:引入依赖
org.springframework.retry
spring-retry
org.aspectj
aspectjweaver
io.github.resilience4j
resilience4j-spring-boot2
1.9.5
第三步:yml 配置
spring:retry:
max-attempts: 3 # 最多重试3次
backoff:
delay: 1000
resilience4j:
circuitbreaker:
instances:
databaseService:
failure-rate-threshold: 50 # 失败率50%触发熔断
wait-duration-in-open-state: 60000 # 熔断60秒后尝试恢复
第四步:业务层注解
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;import io.github.resilience4j.retry.annotation.Retry;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
@Service
public class OrderService {
private final OrderRepository orderRepository;
// 构造方法注入(省略)
@Retry(name = "databaseService")
@CircuitBreaker(name = "databaseService", fallbackMethod = "queryOrderFallback")
public List queryOrder(Long userId) {
return orderRepository.findByUserId(userId);
// 降级方法
public List queryOrderFallback(Long userId, Exception e) {
log.error("查询失败,触发降级:", e);
return Collections.emptyList();
关键提醒
- 连接超时别设太长,5 秒足够;
- 熔断是 “保险丝”,数据库故障时保护服务不雪崩!
默认不缓存 JS、CSS、图片,用户每次访问都重新下载,页面加载慢,还浪费服务器带宽!
生产配置(直接复制)第一步:SpringBoot 配置
spring:web:
resources:
static-locations: classpath:/static/,classpath:/public/
cache:
cachecontrol:
max-age: 604800 # 缓存7天
cache-public: true
第二步:Nginx 配置(推荐)
server {listen 80;
server_name static.xxx.com; # 静态资源独立域名
root /usr/share/nginx/html/static;
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires 7d;
add_header Cache-Control "public, max-age=604800";
access_log off;
gzip on; # 启用压缩
关键提醒
- 静态资源加版本号(如 app.1.0.0.js),更新时改版本号;
- 用独立域名存静态资源,加载更快!
SpringBoot 默认配置只适合开发 / 测试!以上 8 个配置覆盖性能、安全、稳定性,直接复制到生产项目,部署前逐一检查,避免连夜加班!
你在项目中曾因默认配置踩过哪些坑?欢迎在评论区分享经历!觉得有用的话,点赞 + 收藏 + 关注,下次配置直接对照用,少走弯路!
#JAVA培训##JAVA开发#
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.