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 请求。它负责通过传递 HttpServletRequest
和 HttpServletResponse
对象作为参数来调用处理器。处理器随后执行应用逻辑并返回一个 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 项目 中找到。