Mastering Spring Boot 3: Real‑World Cases, ThreadLocal, Async & More
This article presents a continuously updated collection of over 90 practical Spring Boot 3 examples, covering request/response access, ThreadLocal handling, async interceptors, request metadata extraction, custom type converters, 404 error handling, and related source code to help developers build robust backend services.
Spring Boot 3 practical case collection now includes over 90 real‑world examples, with a permanent update promise for the latest techniques and source code.
1. Accessing Request/Response Objects
Two ways are demonstrated: injecting HttpServletRequest and HttpServletResponse as fields or constructor parameters, and obtaining them via method parameters. For non‑controller components, RequestContextHolder with ServletRequestAttributes can retrieve the current request/response, and ThreadLocal inheritance can be enabled for child threads.
<code>@RestController
public class UserController {
private final HttpServletRequest request;
private final HttpServletResponse response;
public UserController(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
}
</code> <code>@GetMapping
public ResponseEntity<Object> query(HttpServletRequest request, HttpServletResponse response) {
// ...
}
</code> <code>ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
HttpServletResponse response = attributes.getResponse();
</code>To allow child threads to inherit the ThreadLocal context, configure the dispatcher servlet:
<code>@Bean(name = "dispatcherServletRegistration")
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
// enable inheritance
dispatcherServlet.setThreadContextInheritable(true);
return registration;
}
</code>Testing interface demonstrates that the request and response are available in the main thread, but accessing them from a child thread without inheritance throws an error because the objects have been recycled.
2. Asynchronous Interceptor
Spring provides AsyncWebRequestInterceptor to handle asynchronous requests. The interceptor’s afterConcurrentHandlingStarted method is called when async processing begins, while the usual preHandle , postHandle , and afterCompletion are invoked after the async task finishes.
<code>public class LogInterceptor implements AsyncWebRequestInterceptor {
@Override
public void preHandle(WebRequest request) throws Exception {
System.err.printf("AsyncWebRequestInterceptor >>> %s, %s, start handling%n",
System.currentTimeMillis(), Thread.currentThread().getName());
}
@Override
public void postHandle(WebRequest request, ModelMap model) throws Exception {
System.err.printf("AsyncWebRequestInterceptor >>> %s, postHandle%n",
Thread.currentThread().getName());
}
@Override
public void afterCompletion(WebRequest request, Exception ex) throws Exception {
System.err.printf("AsyncWebRequestInterceptor >>> %s afterCompletion%n",
Thread.currentThread().getName());
}
@Override
public void afterConcurrentHandlingStarted(WebRequest request) {
System.err.printf("AsyncWebRequestInterceptor >>> %s, %s, async handling%n",
System.currentTimeMillis(), Thread.currentThread().getName());
}
}
</code>Register the interceptor:
<code>@Component
public class WebInterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addWebRequestInterceptor(new LogInterceptor()).addPathPatterns("/admin/**");
}
}
</code>Test async endpoint:
<code>@GetMapping("/async")
public Callable<String> async() {
System.err.println("async interface...");
return () -> {
System.err.printf("%s, %s - executing task%n", System.currentTimeMillis(), Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(3);
return "async data";
};
}
</code>3. Obtaining Current Request Information
Spring MVC stores useful data in the request attributes. Examples:
Current request path: String key = ServletRequestPathUtils.PATH; String requestPath = request.getAttribute(key);
Best‑matching pattern: String key = HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE; String pathPattern = request.getAttribute(key);
Path variables: String key = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE; Map<String, String> vars = (Map<String, String>) request.getAttribute(key);
4. Registering Custom Type Converters
Spring provides many built‑in converters; custom converters can be added via WebMvcConfigurer.addFormatters or by declaring them as beans. Example of a simple enum‑to‑integer converter:
<code>@Component
public class EnumConverter implements Converter<Sex, Integer> {
@Override
public Integer convert(Sex source) {
return source == null ? 0 : source.getCode();
}
}
</code>Note: custom converters do not support property‑based type conversion.
5. Handling Missing Endpoints (404)
When an endpoint does not exist, Spring returns a default 404 page. You can provide custom 404.html or 4xx.html files, or handle the exception globally:
<code>@RestControllerAdvice
public class GlobalExceptionAdvice {
@ExceptionHandler(NoHandlerFoundException.class)
public ResponseEntity<Object> handleNotFound(NoHandlerFoundException e) {
return ResponseEntity.ok(Map.of("code", -1, "message", "Interface not found"));
}
}
</code>The article concludes with a call to like, share, and collect the content.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
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.