1. 概述

本文将深入探讨 Spring 框架中提供的各种 HandlerAdapter 实现类型。

2. 什么是 HandlerAdapter?

HandlerAdapter 本质上是一个接口,它让 Spring MVC 能够以非常灵活的方式处理 HTTP 请求。

它与 HandlerMapping 协同工作,后者负责将方法映射到特定 URL。

DispatcherServlet 随后使用 HandlerAdapter 来调用这些方法。Servlet 不会直接调用方法——它充当自身与处理器对象之间的桥梁,从而实现松耦合设计。

让我们看看该接口中的几个核心方法:

public interface HandlerAdapter {
    boolean supports(Object handler);
    
    ModelAndView handle(
      HttpServletRequest request,
      HttpServletResponse response, 
      Object handler) throws Exception;
    
    long getLastModified(HttpServletRequest request, Object handler);
}

supports 方法用于检查是否支持特定的处理器实例。在调用 handle() 方法之前必须先调用此方法,以确保处理器实例是否受支持。

handle 方法用于处理特定的 HTTP 请求。它负责通过传递 HttpServletRequestHttpServletResponse 对象作为参数来调用处理器。处理器随后执行应用逻辑并返回一个 ModelAndView 对象,该对象由 DispatcherServlet 进一步处理。

3. Maven 依赖

首先需要在 pom.xml 中添加以下依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>6.1.5</version>
</dependency>

最新版本的 spring-webmvc 工件可在 这里 查找。

4. HandlerAdapter 类型

4.1 SimpleControllerHandlerAdapter

这是 Spring MVC 默认注册的 HandlerAdapter。它处理实现了 Controller 接口的类,并将请求转发给控制器对象。

如果 Web 应用只使用控制器,则无需配置任何 HandlerAdapter,因为框架默认使用此类处理请求。

让我们定义一个简单的控制器类(使用旧式控制器风格,实现 Controller 接口):

public class SimpleController implements Controller {
    @Override
    public ModelAndView handleRequest(
      HttpServletRequest request, 
      HttpServletResponse response) throws Exception {
        
        ModelAndView model = new ModelAndView("Greeting");
        model.addObject("message", "Dinesh Madhwal");
        return model;
    }
}

对应的 XML 配置:

<beans ...>
    <bean name="/greeting.html"
      class="com.baeldung.spring.controller.SimpleControllerHandlerAdapterExample"/>
    <bean id="viewResolver"
      class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

此适配器的映射类是 BeanNameUrlHandlerMapping

⚠️ 注意:如果在 BeanFactory 中定义了自定义 HandlerAdapter,则不会自动注册此适配器。因此需要在上下文中显式定义它。如果未定义且已配置自定义适配器,将抛出"未指定处理器适配器"的异常。

4.2 SimpleServletHandlerAdapter

此适配器允许任何 Servlet 与 DispatcherServlet 协同处理请求。它通过调用 Servlet 的 service() 方法将请求从 DispatcherServlet 转发到相应的 Servlet 类。

实现 Servlet 接口的 Bean 会自动由此适配器处理。它不是默认注册的,需要在 DispatcherServlet 的配置文件中像普通 Bean 一样注册:

<bean name="simpleServletHandlerAdapter" 
  class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter" />

4.3 AnnotationMethodHandlerAdapter

此适配器类用于执行带有 @RequestMapping 注解的方法。它根据 HTTP 方法和 HTTP 路径映射方法。

此适配器的映射类是 DefaultAnnotationHandlerMapping,用于处理类型级别的 @RequestMapping 注解,而 AnnotationMethodHandlerAdaptor 用于处理方法级别的注解。

这两个类在 DispatcherServlet 初始化时已由框架注册。但如果已定义其他 HandlerAdapter,则需要在配置文件中显式定义它们。

让我们定义一个控制器类:

@Controller
public class AnnotationHandler {
    @RequestMapping("/annotedName")
    public ModelAndView getEmployeeName() {
        ModelAndView model = new ModelAndView("Greeting");        
        model.addObject("message", "Dinesh");       
        return model;  
    }  
}

@Controller 注解表明此类扮演控制器角色。

@RequestMapping 注解将 getEmployeeName() 方法映射到 URL /name

配置此适配器有两种方式,取决于应用使用 Java 配置还是 XML 配置。先看 Java 配置方式:

@ComponentScan("com.baeldung.spring.controller")
@Configuration
@EnableWebMvc
public class ApplicationConfiguration implements WebMvcConfigurer {
    @Bean
    public InternalResourceViewResolver jspViewResolver() {
        InternalResourceViewResolver bean = new InternalResourceViewResolver();
        bean.setPrefix("/WEB-INF/");
        bean.setSuffix(".jsp");
        return bean;
    }
}

如果应用使用 XML 配置,在 Web 应用上下文 XML 中配置此适配器有两种方法。先看 spring-servlet_AnnotationMethodHandlerAdapter.xml 文件中的第一种方法:

<beans ...>
    <context:component-scan base-package="com.baeldung.spring.controller" />
    <bean 
      class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
    <bean 
      class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
    <bean id="viewResolver"
      class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

<context:component-scan /> 标签用于指定扫描控制器类的包。

再看第二种方法:

<beans ...>
    <mvc:annotation-driven/>
    <context:component-scan base-package="com.baeldung.spring.controller" />
    <bean id="viewResolver"
      class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

<mvc:annotation-driven> 标签会自动向 Spring MVC 注册这两个类。此适配器在 Spring 3.2 中已废弃,Spring 3.1 引入了新的 RequestMappingHandlerAdapter

4.4 RequestMappingHandlerAdapter

此适配器类在 Spring 3.1 中引入,Spring 3.2 废弃了 AnnotationMethodHandlerAdaptor

它与 RequestMappingHandlerMapping 类配合使用,执行带有 @RequestMapping 注解的方法

RequestMappingHandlerMapping 用于维护请求 URI 到处理器的映射。获得处理器后,DispatcherServlet 将请求分派给相应的适配器,后者再调用 handlerMethod()

在 Spring 3.1 之前的版本中,类型级和方法级映射分两个阶段处理:

  • 第一阶段由 DefaultAnnotationHandlerMapping 选择控制器
  • 第二阶段由 AnnotationMethodHandlerAdapter 调用实际方法

从 Spring 3.1 开始,只有一个阶段:同时识别控制器和需要调用的方法来处理请求。

让我们定义一个简单的控制器类:

@Controller
public class RequestMappingHandler {
    
    @RequestMapping("/requestName")
    public ModelAndView getEmployeeName() {
        ModelAndView model = new ModelAndView("Greeting");        
        model.addObject("message", "Madhwal");        
        return model;  
    }  
}

同样有两种配置方式。先看 Java 配置:

@ComponentScan("com.baeldung.spring.controller")
@Configuration
@EnableWebMvc
public class ServletConfig implements WebMvcConfigurer {
    @Bean
    public InternalResourceViewResolver jspViewResolver() {
        InternalResourceViewResolver bean = new InternalResourceViewResolver();
        bean.setPrefix("/WEB-INF/");
        bean.setSuffix(".jsp");
        return bean;
    }
}

如果使用 XML 配置,在 Web 应用上下文 XML 中有两种方法。先看 spring-servlet_RequestMappingHandlerAdapter.xml 文件中的第一种方法:

<beans ...>
    <context:component-scan base-package="com.baeldung.spring.controller" />
    
    <bean 
      class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
    
    <bean
      class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
    
    <bean id="viewResolver"
      class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

再看第二种方法:

<beans ...>
    <mvc:annotation-driven />
    
    <context:component-scan base-package="com.baeldung.spring.controller" />
    
    <bean id="viewResolver"
      class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

<mvc:annotation-driven> 标签会自动向 Spring MVC 注册这两个类。

如果需要自定义 RequestMappingHandlerMapping,则需从应用上下文 XML 中移除此标签,并在应用上下文 XML 中手动配置。

4.5 HttpRequestHandlerAdapter

此适配器用于处理 HttpRequest 的处理器。它实现 HttpRequestHandler 接口,该接口包含单个 handleRequest() 方法用于处理请求并生成响应。

此方法返回类型为 void,不像其他适配器那样生成 ModelAndView。它主要用于生成二进制响应,不生成要渲染的视图。

5. 运行应用

如果应用部署在 localhost 端口 8082,上下文根为 spring-mvc-handlers

http://localhost:8082/spring-mvc-handlers/

6. 总结

本文讨论了 Spring 框架中可用的各种 HandlerAdapter 类型。

大多数开发者可能坚持使用默认配置,但当我们需要超越基础功能时,理解框架的灵活性是非常值得的。

本教程的源代码可在 GitHub 项目 中找到。


原始标题:Types of Spring HandlerAdapters