《Spring Boot 3实战案例合集》现已囊括超过90篇精选实战文章,并且此合集承诺将永久持续更新,为您带来最新的技术与实战案例。订阅用户将特别获赠文末中所有MD文档(详尽学习笔记),以及完整的项目源码,助您在学习道路上畅通无阻。
《Spring Boot 3实战案例锦集》PDF电子书现已出炉!
🎉🎉我们精心打造的《Spring Boot 3实战案例锦集》PDF电子书现已正式完成,目前已经有94个案例,后续还将继续更新。文末有电子书目录。
💪💪永久更新承诺:
我们郑重承诺,所有订阅合集的粉丝都将享受永久免费的后续更新服务。随着Spring相关技术的更新升级,我们的电子书也将持续更新,确保您始终掌握最前沿、最实用的技术知识。
💌💌如何获取:
订阅我们的合集《点我订阅》,并通过私信联系我们,我们将第一时间将电子书发送给您。
环境:SpringBoot3.2.5
1. 获取请求/响应对象
在编写Controller中,我们通常可以通过如下的2种方式直接获取Request和Response对象,如下示例:
@RestControllerpublic class UserController {private final HttpServletRequest request ;private final HttpServletResponse response ;public ContextFilterController(HttpServletRequest request, HttpServletResponse response) {this.request = request;this.response = response;}}
直接在Controller中注入对象,你也可以在方法参数中获取:
public ResponseEntity<Object> query(HttpServletRequest request,HttpServletResponse response)// ...}
如果你需要在其它组件中获取该对象,如果通过方法参数传递那就太麻烦了,这时候我们就可以使用如下方式:
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes() ;HttpServletRequest request = attributes.getRequest() ;HttpServletResponse response = attributes.getResponse() ;
直接通过ThreadLocal获取,而这个数据的存入则是由DispatcherServlet完成,不过默认还有一个RequestContextFilter也会做这个事,但是会被该Servlet覆盖。
ThreadLocal绑定当前线程,如果遇到子线程怎么办呢?
如果希望在子线程中也能获取当前上下文,那么你需要进行如下的配置才可:
(name = "dispatcherServletRegistration")DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,webMvcProperties.getServlet().getPath());// 设置从父线程中继承dispatcherServlet.setThreadContextInheritable(true) ;// ...return registration;}
测试接口
@GetMappingpublic ResponseEntity<Object> context() throws Exception {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes() ;HttpServletRequest request = attributes.getRequest() ;HttpServletResponse response = attributes.getResponse() ;System.err.printf("%s - %s, %s%n", Thread.currentThread().getName(), request, response) ;Thread t = new Thread(() -> {ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes() ;HttpServletRequest req = attr.getRequest() ;HttpServletResponse resp = attr.getResponse() ;System.err.printf("%s - %s, %s%n", Thread.currentThread().getName(), req, resp) ;}, "T1") ;t.start() ;return ResponseEntity.ok("success") ;}
控制台输出如下

成功获取
警告!如下方式操作,你将收获一个错误
Thread t = new Thread(() -> {try {TimeUnit.SECONDS.sleep(1) ;} catch (Exception e) {e.printStackTrace();}ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes() ;HttpServletRequest req = attr.getRequest() ;System.err.println(req.getParameter("token")) ;}, "T1") ;
如上代码,休眠1s后,在从request中获取数据,将抛出如下错误

这是因为主线程已经将Request,Response对象回收了。
总结:不建议开启子线程共享。
2. 异步拦截器
Spring提供一个专门处理异步请求的拦截器AsyncWebRequestInterceptor,该接口包含一个在处理异步请求期间被调用的回调方法。
当处理器开始处理异步请求时,DispatcherServlet会像平常一样退出,而不调用postHandle和afterCompletion方法,因为请求处理的结果(例如ModelAndView)在当前线程中不可用,且处理尚未完成。在这种情况下,会调用afterConcurrentHandlingStarted(WebRequest)方法,允许实现执行诸如清理线程绑定属性之类的任务。
当异步处理完成时,请求会被分发到容器中进行进一步处理。在这个阶段,DispatcherServlet会像平常一样调用preHandle、postHandle和afterCompletion方法。
public class LogInterceptor implements AsyncWebRequestInterceptor {// 请求一开始会执行一次public void preHandle(WebRequest request) throws Exception {System.err.printf("AsyncWebRequestInterceptor >>> %s, %s, 开始处理%n", System.currentTimeMillis(), Thread.currentThread().getName()) ;}// 当异步请求结束时执行public void postHandle(WebRequest request, ModelMap model) throws Exception {System.err.printf("AsyncWebRequestInterceptor >>> %s, postHandle%n", Thread.currentThread().getName()) ;}// 当异步请求结束时执行public void afterCompletion(WebRequest request, Exception ex) throws Exception {System.err.printf("AsyncWebRequestInterceptor >>> %s afterCompletion%n", Thread.currentThread().getName()) ;}// 异步请求开始时执行public void afterConcurrentHandlingStarted(WebRequest request) {System.err.printf("AsyncWebRequestInterceptor >>> %s, %s, 异步处理%n", System.currentTimeMillis(), Thread.currentThread().getName()) ;}}
注册异步拦截器
public class WebInterceptorConfig implements WebMvcConfigurer{public void addInterceptors(InterceptorRegistry registry) {registry.addWebRequestInterceptor(new LogInterceptor()).addPathPatterns("/admin/**") ;}}
下面通过如下异步接口进行测试
("/async")public Callable<String> async() {System.err.println("async interface...") ;return new Callable<String>() {public String call() throws Exception {System.err.printf("%s, %s - 执行任务%n", System.currentTimeMillis(), Thread.currentThread().getName()) ;TimeUnit.SECONDS.sleep(3) ;return "异步数据" ;}};}
输出结果

等待异步处理完成以后再执行preHandle、postHandle和afterCompletion方法。
实际整个异步请求从开始到结束,preHandle是执行了两回。
3. 获取当前请求相关信息
Spring MVC在处理一个请求时,会为我们做很多的事,其中会往Request对象设置一些非常有用的数据,如下所示:
获取当前的请求路径
String key = ServletRequestPathUtils.PATH ;String requestPath = request.getAttribute(key) ;
获取当前请求最佳匹配的路径(Controller中定义的路径)
String key = HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE;String pathPattern = request.getAttribute(key) ;
返回:/params/{type}/{id}
获取当前请求中的路径参数值
String key = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE ;String pathPattern = request.getAttribute(key) ;
返回:{id=666, type=s0}
4. 类型转换器注册方式
Spring 本身提供了非常多的类型转换器,绝大多数情况下我们是不需要再自定义类型转换器的。如果确实需要自定义类型转换器,那么我们通常会通过如下的方法进行注册自定义的转换器:
public class TypeConvertConfig implements WebMvcConfigurer {public void addFormatters(FormatterRegistry registry) {registry.addConverter(new EnumConverter()) ;}}
感觉是不是有点麻烦。其实我们可以直接定义类型转换器为Bean对象即可,并且支持:GenericConverter,Converter,Printer,Parser类型的转换器。
public class EnumConverter implements Converter<Sex, Integer> {public Integer convert(Sex source) {if (source == null) {return 0 ;}return source.getCode() ;}}
注意:这里的自定义转换器并不支持有关属性配置的类型转换。
关于更多的类型转换器的使用,请查看下面文章:
5. 接口不存在时特殊处理
当我们访问的接口不存在时,默认输出如下:

或者我们也可以在如下位置提供对应的404.html或4xx.html页面

如上位置添加页面后,当出现404错误,将会调转到该页面。
其实,我们还可以通过如下全局异常的方式处理404错误,默认如果出现404错误会抛出NoHandlerFoundException异常。
public class GlobalExceptionAdvice {public ResponseEntity<Object> noHandlerFount(NoHandlerFoundException e) {return ResponseEntity.ok(Map.of("code", -1, "message", "接口不存在")) ;}}
当发生404后,页面展示:

以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏
推荐文章
优雅!Spring 基于 Plugin 插件开发(官方推荐)
弃用HTTP!Spring Boot 集成 GRPC 优化接口调用性能
高级开发!自定义注解100行代码实现 @Resource/@Autowired 注入功能
接口优化!Spring Boot 集成 Protobuf 并使用 RestTemplate 实现微服务间通信
SpringBoot一个注解轻松实现多版本Rest API接口控制
SpringBoot3第三方接口调用10种方式,最后一种你肯定没用过

















