Avoid Hidden SpringBoot Pitfalls: Essential Config Tweaks for Production
SpringBoot’s “convention over configuration” simplifies development, but its default settings—such as limited Tomcat connections, minimal HikariCP pool size, unchecked JPA lazy loading, unrestricted file upload limits, and inadequate logging and caching—can cause performance bottlenecks and runtime failures, so this guide details essential configuration adjustments to ensure robust, production‑ready applications.
Introduction
When SpringBoot first emerged, its promise of "out‑of‑the‑box" and "convention over configuration" attracted many developers. While the defaults let you start quickly, they also hide assumptions that may not fit production environments, leading to hidden failures.
Tomcat Connection Pool
SpringBoot uses Tomcat as the default web container, but the default connection pool limits become a bottleneck under high concurrency.
server:
tomcat:
max-connections: 10000 # 最大连接数
threads:
max: 800 # 最大工作线程数
min-spare: 100 # 最小空闲线程数
accept-count: 100 # 等待队列长度
connection-timeout: 20000The default maximum connections and threads are only 200 each; exceeding this causes request queuing.
Database Connection Pool
SpringBoot defaults to HikariCP, yet the default pool size (10) is insufficient for most production workloads.
spring:
datasource:
hikari:
maximum-pool-size: 50
minimum-idle: 10
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
leak-detection-threshold: 60000Pay special attention to leak-detection-threshold, which is disabled by default and can hide connection leaks.
JPA Lazy Loading
SpringBoot enables lazy loading for JPA entities, which often leads to N+1 query problems.
@Entity
public class User {
@Id
private Long id;
@OneToMany(fetch = FetchType.LAZY) // 默认就是LAZY
private List<Order> orders;
}Fetching a list of users triggers a separate query for each orders collection. To avoid this, use @EntityGraph, JOIN FETCH, or explicit queries:
@Query("SELECT u FROM User u LEFT JOIN FETCH u.orders")
List<User> findAllWithOrders();Jackson Time‑Zone Serialization
Jackson inherits the system time‑zone, which can cause inconsistent timestamps across distributed deployments.
spring:
jackson:
time-zone: GMT+8
date-format: yyyy-MM-ddHH:mm:ss
serialization:
write-dates-as-timestamps: falseLog Configuration
SpringBoot uses Logback with no file rotation or size limits by default, leading to ever‑growing log files.
logging:
file:
name: app.log
logback:
rollingpolicy:
max-file-size: 100MB
max-history: 30
total-size-cap: 3GBAdjust log levels and enable rolling to prevent disk exhaustion.
Cache Configuration
The default @Cacheable implementation uses a plain ConcurrentHashMap without expiration, which can cause unbounded memory growth.
spring:
cache:
type: caffeine
caffeine:
spec: maximumSize=10000,expireAfterWrite=600sSwitching to Caffeine provides size limits and expiration.
Monitoring Endpoints
SpringBoot Actuator exposes many endpoints; exposing them in production can leak sensitive information.
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: when-authorizedExpose only necessary endpoints and secure them appropriately.
File Upload Size Limits
The default limits (1 MB per file, 10 MB total) are often too low for real‑world use, causing MaxUploadSizeExceededException.
spring:
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
file-size-threshold: 2KB
location: /tmp
resolve-lazily: falseAdjust thresholds to balance memory usage and performance.
Async Thread Pool Configuration
Using @Async defaults to SimpleAsyncTaskExecutor, which creates a new thread per task, exhausting resources under load.
spring:
task:
execution:
pool:
core-size: 8
max-size: 16
queue-capacity: 100
keep-alive: 60s
thread-name-prefix: async-task-
scheduling:
pool:
size: 4
thread-name-prefix: scheduling-Configure core and max sizes based on CPU‑bound or I/O‑bound workloads.
Static Resource Caching Strategy
SpringBoot does not set HTTP cache headers for static assets, causing browsers to re‑download unchanged files.
spring:
web:
resources:
cache:
cachecontrol:
max-age: 365d
cache-public: true
chain:
strategy:
content:
enabled: true
paths: /**
cache: true
static-locations: classpath:/static/Enabling content versioning (e.g., style-abc123.css) lets browsers cache unchanged files indefinitely.
Database Transaction Timeout
Without a timeout, long‑running transactions hold locks and can block other operations.
@Transactional(timeout = 30, rollbackFor = Exception.class)
public void batchProcess(List<Data> dataList) {
// 分批处理,避免长事务
int batchSize = 100;
for (int i = 0; i < dataList.size(); i += batchSize) {
List<Data> batch = dataList.subList(i,
Math.min(i + batchSize, dataList.size()));
processBatch(batch);
}
}Split large jobs into smaller transactions and set appropriate timeouts to release locks promptly.
Conclusion
SpringBoot’s conventions save development effort, but each default hides assumptions that may not suit production. Proactively reviewing and tuning configurations—connection pools, caching, logging, async execution, and more—prevents hidden failures and ensures a stable, high‑performance system.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
