紧急提醒!SpringBoot 这 8 个默认配置不改,上线必崩!

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开发##技术文档编写#

© 版权声明

相关文章

2 条评论

您必须登录才能参与评论!
立即登录
  • 头像
    一花一世界 读者

    第一个jvm内存,spring表示不被这个锅

    无记录
  • 头像
    小代 读者

    收藏了,感谢分享

    无记录