手把手教你搞懂Spring MVC的HandlerAdapter:从“媒婆”到“翻译官”的奇幻漂流

120 阅读4分钟

手把手教你搞懂Spring MVC的HandlerAdapter:从“媒婆”到“翻译官”的奇幻漂流


一、介绍:HandlerAdapter是个什么鬼?

如果把Spring MVC比作一家餐厅,DispatcherServlet是前台服务员,Controller是后厨大厨,那HandlerAdapter就是那个拿着菜单在后厨跑来跑去的传菜员——它要确保服务员和大厨之间语言互通

通俗来说:
不同的处理器(比如@ControllerHttpRequestHandlerServlet)写法不同,但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内部通过HandlerMethodArgumentResolverHandlerMethodReturnValueHandler实现参数解析和返回值处理,堪称反射狂魔


五、对比:四大“翻译官”的战斗力PK

对比维度RequestMappingHandlerAdapterSimpleControllerHandlerAdapterHttpRequestHandlerAdapterSimpleServletHandlerAdapter
灵活性⭐⭐⭐⭐⭐(注解驱动)⭐⭐(接口约束)⭐⭐⭐(直接操作流)⭐(纯Servlet)
性能较高(缓存映射关系)一般
使用难度高(需手动处理)
推荐指数⭐⭐⭐⭐⭐⭐(怀旧专用)⭐⭐⭐(特定场景)⭐(兼容遗留代码)

六、避坑指南:那些年我们掉过的坑

  1. 404的幽灵
    症状:明明配置了Controller,却返回404。
    病因:对应的HandlerAdapter没注册。
    药方:检查是否手动关闭了自动配置,或在WebMvcConfigurer中添加适配器。

  2. 时间转换玄学
    症状:前端传2023-01-01,后端收到null
    病因@RequestParam LocalDate没注册Converter。
    药方:添加@Configuration + 实现WebMvcConfigurer.addFormatters()

  3. JSON反序列化失败
    症状:POST JSON数据时,对象属性全是null。
    病因:没配置消息转换器(如MappingJackson2HttpMessageConverter)。
    药方:检查是否添加了Jackson依赖,或手动配置HttpMessageConverters


七、最佳实践:做个优雅的“甩手掌柜”

  1. 能用注解就别用接口
    @Controller + @RequestMapping组合灵活度最高,拥抱现代Spring。

  2. 按需手动注册适配器
    除非要兼容老代码,否则别碰SimpleControllerHandlerAdapter这些“古董”。

  3. 统一时间格式处理
    全局配置@DateTimeFormat或实现WebMvcConfigurer

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatter(new DateFormatter("yyyy-MM-dd"));
    }
    
  4. 自定义参数解析器
    实现HandlerMethodArgumentResolver,处理特殊参数(如解密token)。


八、面试考点:如何让面试官直呼内行?

高频问题

  1. HandlerAdapter的作用是什么?
    :桥接DispatcherServlet与不同处理器,解耦请求处理逻辑。

  2. 如何自定义HandlerAdapter?
    :实现接口,注册到Spring容器,通常需要调整优先级。

  3. 为什么@RequestBody需要Jackson?
    :因为RequestMappingHandlerAdapter依赖HttpMessageConverter处理JSON。

  4. 处理静态资源为什么要用ResourceHandlerRegistry?
    :底层通过HttpRequestHandlerAdapter映射到资源处理器。


九、总结:HandlerAdapter的“职场哲学”

HandlerAdapter就像职场中的万能适配器——

  • 对上(DispatcherServlet)统一接口
  • 对下(各种Handler)灵活应对
  • 深藏功与名,却撑起整个Spring MVC的调度体系

最后送大家一句改编诗:
“横看成岭侧成峰,HandlerAdapter各不同。不识Spring真面目,只缘未读此文中。” 😎