手把手教你搞懂Spring MVC的HandlerAdapter:从“媒婆”到“翻译官”的奇幻漂流
一、介绍:HandlerAdapter是个什么鬼?
如果把Spring MVC比作一家餐厅,DispatcherServlet是前台服务员,Controller是后厨大厨,那HandlerAdapter就是那个拿着菜单在后厨跑来跑去的传菜员——它要确保服务员和大厨之间语言互通!
通俗来说:
不同的处理器(比如@Controller、HttpRequestHandler、Servlet)写法不同,但DispatcherServlet只有一个。这时候就需要HandlerAdapter来统一调度,把HTTP请求转化为处理器能听懂的语言,再把处理结果翻译成ModelAndView。
官方定义:
HandlerAdapter是Spring MVC中用于桥接处理器(Handler)和前端控制器(DispatcherServlet)的核心接口,负责实际调用处理器方法并返回结果。
二、用法:四大“翻译官”的江湖地位
Spring MVC默认注册了四大HandlerAdapter,按优先级排序:
| 适配器类 | 支持的处理器类型 | 使用场景 |
|---|---|---|
RequestMappingHandlerAdapter | @Controller注解类 | 现代Spring MVC的绝对主力 |
HttpRequestHandlerAdapter | 实现HttpRequestHandler接口的类 | 处理静态资源(如Tomcat默认) |
SimpleControllerHandlerAdapter | 实现Controller接口的类 | 早期Spring MVC写法 |
SimpleServletHandlerAdapter | 普通Servlet类 | 兼容老项目Servlet代码 |
配置方式(一般不需要手动配,自动注册优先级最高):
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureHandlerAdapters(HandlerAdapterRegistry registry) {
registry.add(new MyCustomAdapter()); // 自定义适配器
}
}
三、案例:看看“翻译官”怎么干活
案例1:@Controller注解的处理器
@RestController
public class CatController {
@GetMapping("/meow")
public String meow() {
return "喵喵喵";
}
}
适配器:RequestMappingHandlerAdapter(自动处理@GetMapping)
案例2:实现Controller接口的老古董
public class DogController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
return new ModelAndView("dog", "sound", "汪汪汪");
}
}
适配器:SimpleControllerHandlerAdapter
案例3:处理静态资源的HttpRequestHandler
public class ResourceHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) {
// 直接写响应流,比如返回图片
}
}
适配器:HttpRequestHandlerAdapter
四、原理:解剖“翻译官”的脑子
HandlerAdapter接口源码:
public interface HandlerAdapter {
boolean supports(Object handler); // 判断是否支持该处理器
ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
工作流程图:
DispatcherServlet收到请求
↓
遍历所有HandlerAdapter,调用supports()方法
↓
找到第一个支持的Adapter,调用handle()方法
↓
返回ModelAndView或直接写Response
黑科技:
RequestMappingHandlerAdapter内部通过HandlerMethodArgumentResolver和HandlerMethodReturnValueHandler实现参数解析和返回值处理,堪称反射狂魔!
五、对比:四大“翻译官”的战斗力PK
| 对比维度 | RequestMappingHandlerAdapter | SimpleControllerHandlerAdapter | HttpRequestHandlerAdapter | SimpleServletHandlerAdapter |
|---|---|---|---|---|
| 灵活性 | ⭐⭐⭐⭐⭐(注解驱动) | ⭐⭐(接口约束) | ⭐⭐⭐(直接操作流) | ⭐(纯Servlet) |
| 性能 | 较高(缓存映射关系) | 一般 | 高 | 高 |
| 使用难度 | 低 | 中 | 中 | 高(需手动处理) |
| 推荐指数 | ⭐⭐⭐⭐⭐ | ⭐(怀旧专用) | ⭐⭐⭐(特定场景) | ⭐(兼容遗留代码) |
六、避坑指南:那些年我们掉过的坑
-
404的幽灵
症状:明明配置了Controller,却返回404。
病因:对应的HandlerAdapter没注册。
药方:检查是否手动关闭了自动配置,或在WebMvcConfigurer中添加适配器。 -
时间转换玄学
症状:前端传2023-01-01,后端收到null。
病因:@RequestParam LocalDate没注册Converter。
药方:添加@Configuration+ 实现WebMvcConfigurer.addFormatters()。 -
JSON反序列化失败
症状:POST JSON数据时,对象属性全是null。
病因:没配置消息转换器(如MappingJackson2HttpMessageConverter)。
药方:检查是否添加了Jackson依赖,或手动配置HttpMessageConverters。
七、最佳实践:做个优雅的“甩手掌柜”
-
能用注解就别用接口
@Controller+@RequestMapping组合灵活度最高,拥抱现代Spring。 -
按需手动注册适配器
除非要兼容老代码,否则别碰SimpleControllerHandlerAdapter这些“古董”。 -
统一时间格式处理
全局配置@DateTimeFormat或实现WebMvcConfigurer:@Override public void addFormatters(FormatterRegistry registry) { registry.addFormatter(new DateFormatter("yyyy-MM-dd")); } -
自定义参数解析器
实现HandlerMethodArgumentResolver,处理特殊参数(如解密token)。
八、面试考点:如何让面试官直呼内行?
高频问题:
-
HandlerAdapter的作用是什么?
答:桥接DispatcherServlet与不同处理器,解耦请求处理逻辑。 -
如何自定义HandlerAdapter?
答:实现接口,注册到Spring容器,通常需要调整优先级。 -
为什么@RequestBody需要Jackson?
答:因为RequestMappingHandlerAdapter依赖HttpMessageConverter处理JSON。 -
处理静态资源为什么要用ResourceHandlerRegistry?
答:底层通过HttpRequestHandlerAdapter映射到资源处理器。
九、总结:HandlerAdapter的“职场哲学”
HandlerAdapter就像职场中的万能适配器——
- 对上(DispatcherServlet)统一接口
- 对下(各种Handler)灵活应对
- 深藏功与名,却撑起整个Spring MVC的调度体系
最后送大家一句改编诗:
“横看成岭侧成峰,HandlerAdapter各不同。不识Spring真面目,只缘未读此文中。” 😎