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 排查文件,不然崩溃后无迹可寻!
二、Redis 缓存:默认序列化 + 无连接池 = 性能灾难!
致命坑点
默认用 Java 原生序列化,数据又大又乱码;无连接池,并发高了直接超时;缓存无过期时间,Redis 内存迟早撑爆!
生产配置(直接复制)
第一步:引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.7.15</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
第二步: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 共享:分布式部署,用户频繁登录!
致命坑点
默认 Session 存服务器内存,多实例部署时,用户切换实例就需重新登录,体验极差,投诉不断!
生产配置(直接复制)
第一步:引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
第二步: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<T> implements Serializable {
private int code; // 200成功,其他失败
private String msg;
private T data;
public static <T> Result<T> success() {
return new Result<>(200, “操作成功”, null);
}
public static <T> Result<T> success(T data) {
return new Result<>(200, “操作成功”, data);
}
public static <T> Result<T> 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<Void> handle404(NoHandlerFoundException e) {
log.error(“接口不存在:”, e);
return Result.fail(404, “请求的接口不存在”);
}
// 参数校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> 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<Void> 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
第二步:引入依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.9.5</version>
</dependency>
第三步: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<Order> queryOrder(Long userId) {
return orderRepository.findByUserId(userId);
}
// 降级方法
public List<Order> 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开发##技术文档编写#





第一个jvm内存,spring表示不被这个锅
收藏了,感谢分享