Backend Development 10 min read

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 Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Spring Boot 3: Real‑World Cases, ThreadLocal, Async & More

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&lt;Object&gt; 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&lt;MultipartConfigElement&gt; 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.

Console output
Console output

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&lt;String&gt; 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>
Async processing output
Async processing output

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&lt;String, String&gt; vars = (Map&lt;String, String&gt;) 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&lt;Sex, Integer&gt; {
    @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&lt;Object&gt; handleNotFound(NoHandlerFoundException e) {
        return ResponseEntity.ok(Map.of("code", -1, "message", "Interface not found"));
    }
}
</code>
Custom 404 page
Custom 404 page

The article concludes with a call to like, share, and collect the content.

JavaBackend DevelopmentSpring Booterror handlingAsync ProcessingRequest HandlingType Converter
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.