Unlock Spring Boot’s Hidden Power: 9 Built‑In Features Every Backend Dev Needs
This article walks through nine essential Spring Boot built‑in capabilities—including request logging, content caching, filter execution control, AOP utilities, starter auto‑configuration, flexible property binding, async/scheduled tasks, Actuator monitoring, and SpEL expressions—showing how they boost backend development productivity without extra dependencies.
Spring Boot provides many built‑in utilities that act like ready‑made tools, allowing developers to boost productivity without adding extra dependencies.
1. Full‑chain request logging: CommonsRequestLoggingFilter
During debugging and monitoring, recording complete request information is crucial. Spring Boot’s CommonsRequestLoggingFilter enables detailed logging of query strings, payloads, headers, and client IP.
Core capabilities
Multi‑dimensional data collection: Supports includeQueryString, includePayload, includeHeaders, and client IP.
Flexible log format: Use setAfterMessagePrefix to customize log prefixes for easier classification.
Quick enable
Configure the filter:
@Configuration
public class RequestLoggingConfig {
@Bean
public CommonsRequestLoggingFilter logFilter() {
CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
filter.setIncludeQueryString(true);
filter.setIncludePayload(true);
filter.setMaxPayloadLength(1024);
filter.setAfterMessagePrefix("[REQUEST DATA] ");
return filter;
}
}Set log level in application.properties:
logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=DEBUGLog example:
[REQUEST DATA] POST /api/user, client=192.168.1.1, headers=[Content-Type:application/json], payload={"username":"test","email":"[email protected]"}2. Flexible request/response handling: ContentCaching wrappers
The native HttpServletRequest and
HttpServletResponse> streams can be read only once, limiting scenarios such as logging plus business logic. Spring’s <code>ContentCachingRequestWrapperand ContentCachingResponseWrapper cache the streams, enabling multiple reads.
Core wrappers
Request wrapper ( ContentCachingRequestWrapper ) : Caches request body bytes, allowing downstream controllers to read the body after logging.
Response wrapper ( ContentCachingResponseWrapper ) : Caches response output, permitting modifications (e.g., adding signatures) before the response is committed.
Practical filter implementation
// Request wrapper filter
@Component
public class RequestLogFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
byte[] requestBody = wrappedRequest.getContentAsByteArray();
log.debug("Received request body: {}", new String(requestBody));
chain.doFilter(wrappedRequest, response);
}
}
// Response wrapper filter
@Component
public class ResponseSignFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response);
chain.doFilter(request, wrappedResponse);
byte[] responseBody = wrappedResponse.getContentAsByteArray();
String signature = generateSignature(responseBody);
wrappedResponse.setHeader("X-Response-Signature", signature);
wrappedResponse.copyBodyToResponse();
}
private String generateSignature(byte[] body) {
return Base64.getEncoder().encodeToString(body);
}
}3. Single‑execution guarantee: OncePerRequestFilter base class
In forward or include scenarios, ordinary filters may execute multiple times, causing duplicated logic. Extending OncePerRequestFilter ensures the filter runs only once per request lifecycle.
Core advantages
Avoid duplicate processing: Override doFilterInternal and rely on internal shouldNotFilter checks.
Simplify development: Only need to implement doFilterInternal without manual request‑identification logic.
Applicable scenarios
Logging – prevent repeated logs on forwards.
Security checks – ensure JWT validation runs once.
Performance monitoring – record accurate processing time.
4. AOP development aid: three utility classes
1. AopContext – proxy accessor
When a method call within the same class bypasses the proxy (e.g., @Transactional), AopContext.currentProxy() retrieves the current proxy to trigger aspect logic.
public class ServiceImpl {
@Transactional
public void innerMethod() { /* transaction logic */ }
public void outerMethod() {
((ServiceImpl) AopContext.currentProxy()).innerMethod();
}
}2. AopUtils – proxy type checker
if (AopUtils.isJdkDynamicProxy(proxyObject)) {
// handle JDK dynamic proxy
} else if (AopUtils.isCglibProxy(proxyObject)) {
// handle CGLIB proxy
}3. ReflectionUtils – reflection simplifier
// Access private field
Field field = ReflectionUtils.findField(MyClass.class, "privateField");
ReflectionUtils.makeAccessible(field);
Object value = ReflectionUtils.getField(field, instance);
// Invoke private method
Method method = ReflectionUtils.findMethod(MyClass.class, "privateMethod", String.class);
ReflectionUtils.invokeMethod(method, instance, "arg");5. Dependency management magic: Starter auto‑configuration
Spring Boot’s starter system provides one‑stop dependency bundles. Adding spring-boot-starter-web automatically brings Tomcat, Spring MVC, Jackson, etc.
Version alignment is handled by the spring-boot-dependencies parent POM, preventing conflicts.
Common starter example
Custom starter
Create src/main/resources/META-INF/spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.custom.MyCustomAutoConfigurationImplement auto‑configuration with conditional annotations such as @ConditionalOnClass and @ConditionalOnMissingBean.
6. Configuration simplification: auto‑configuration & placeholders
Spring Boot scans for @EnableAutoConfiguration and auto‑creates beans (e.g., DataSource when spring-boot-starter-jdbc is present). It also binds external properties to POJOs via @ConfigurationProperties, eliminating hard‑coded values.
@Configuration
@ConfigurationProperties(prefix = "app")
public class AppConfig {
private String env;
private DatabaseConfig database;
// getters & setters
public static class DatabaseConfig {
private String url;
private String username;
// ...
}
}YAML example:
app:
env: production
database:
url: jdbc:mysql://localhost:3306/test
username: rootPlaceholders ${} and @Value allow dynamic injection with defaults.
@Value("${app.env:dev}")
private String environment;7. Async & scheduled tasks: annotation‑driven concurrency
Async method call
@Service
public class AsyncService {
@Async("customExecutor")
public CompletableFuture<Void> processAsyncTask(String taskId) {
// time‑consuming logic
return CompletableFuture.completedFuture(null);
}
}
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean("customExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(20);
executor.initialize();
return executor;
}
}Scheduled tasks
@Service
public class ScheduledService {
// Daily at 1 AM
@Scheduled(cron = "0 0 1 * * ?")
public void dailyCleanup() { /* cleanup logic */ }
// Every 5 seconds after previous execution
@Scheduled(fixedDelay = 5000)
public void periodicSync() { /* sync logic */ }
}8. Monitoring & diagnostics: Actuator
Spring Boot Actuator exposes production‑ready endpoints (health, metrics, env, etc.) for observability and dynamic configuration.
Custom metrics
@Autowired
private MeterRegistry meterRegistry;
public void recordOrder(String status) {
meterRegistry.counter("order.processed", "status", status).increment();
}9. Power of expressions: SpEL
Spring Expression Language enables dynamic evaluation in configuration, annotations, and code.
Typical use cases
<!-- Bean definition with SpEL -->
<bean id="userService" class="com.example.UserService">
<property name="defaultTimeout" value="#{T(java.lang.Integer).parseInt('1000')}"/>
</bean> @ConditionalOnExpression("${app.env} == 'prod' && @environment.getProperty('server.port') == 8080")
public class ProdConfig { } @PreAuthorize("hasRole('ADMIN') or @accessService.hasPermission(#userId)")
public void deleteUser(Long userId) { /* security logic */ }Conclusion
Spring Boot’s built‑in features cover the entire development‑to‑operations lifecycle. By thoughtfully combining these tools, developers can eliminate boilerplate code, improve maintainability, and build robust backend services.
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.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.
