目录
DispatcherServlet
DispatcherServlet与tomcat容器
Tomcat容器:Tomcat是一个Web应用服务器,也是一个Servlet/JSP容器。它负责接收来自客户端的HTTP请求,并将这些请求转发给适当的Servlet进行处理。Tomcat还负责处理请求的逻辑,并将Servlet的响应传送回给客户。简而言之,Tomcat是一个邮局的角色,负责管理HTTP请求的传递和处理。
DispatcherServlet:它是Spring MVC框架的核心组件,本质上是一个Servlet。它主要负责接收来自Tomcat容器的请求,并根据请求信息调用相应的Controller进行业务处理。然后,它会将Controller返回的ModelAndView与视图解析器结合,生成最终的响应返回给客户端。在这个过程中,DispatcherServlet就像是一个调度员,协调着整个Web应用程序的流程。
从层次结构上看,Tomcat容器位于底层,负责处理底层的网络通信和协议解析等工作。而DispatcherServlet则位于更高层次,专注于处理业务逻辑和视图渲染等任务。
Spring MVC框架通过DispatcherServlet等组件对Servlet API进行了进一步的封装和扩展,使得开发者可以更加便捷地开发Web应用程序。这种封装和扩展使得Spring MVC在功能性和易用性上超越了传统的Servlet编程模型。
DispatcherServlet的初始化时机
看下面一个例子,我们自己创建Tomcat和DispatcherServlet相关的组件,并进行绑定:
package com.cys.spring.chapter09;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;;
import org.springframework.web.servlet.DispatcherServlet;
@Configuration
@ComponentScan
public class WebConfig {
/** * 内嵌 web 容器工厂 * * @return */
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
/** * 内嵌 DispatcherServlet * * @return */
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
/** * 注册DispatcherServlet,Spring MVC的入口 * * @return */
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
// 第2个参数path表示
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
}
运行后结果如下:
可以看到Tomcat被初始化并启动成功,Spring容器WebApplicationContext也被初始化成功。但是DispatcherServlet却没有被初始化,虽然他被Spring容器创建了。
其实,DispatcherServlet的初始化是由Tomcat来管理的。
当我们访问地址http://127.0.0.1:8080/,再次看下后台日志,如下:
可以看到,这时候DispatcherServlet初始化完成了。
我们也可以让他在Tomcat一启动时就初始化DispatcherServlet,修改WebConfig的dispatcherServletRegistrationBean方法:
package com.cys.spring.chapter09;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
@Configuration
@ComponentScan
public class WebConfig {
/** * 内嵌 web 容器工厂 * * @return */
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
/** * 内嵌 DispatcherServlet * * @return */
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
/** * 注册DispatcherServlet,Spring MVC的入口 * * @return */
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
// 如果 loadOnStartup 的值大于 0,那么 Servlet 容器(如 Tomcat)将在启动时立即加载并初始化 Servlet。
// 数值越小,Servlet 容器加载 Servlet 的优先级就越高。
// 例如,如果有两个 Servlet 的 loadOnStartup 值分别为 1 和 2,那么值为 1 的 Servlet 将先于值为 2 的 Servlet 被加载。
// 如果 loadOnStartup 的值等于 0,那么 Servlet 容器将在第一次请求该 Servlet 时加载并初始化它。
// 如果 loadOnStartup 的值小于 0 或者没有设置(即 loadOnStartup 为负或默认),那么 Servlet 容器将在第一次请求该 Servlet 时加载并初始化它,但这样的 Servlet 不会随容器启动而自动加载。
registrationBean.setLoadOnStartup(1);
return registrationBean;
}
}
DispatcherServlet初始化做了什么
DispatcherServlet的初始化调用了onRefresh
方法
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
其中initStrategies
方法源码如下:
/** * Initialize the strategy objects that this servlet uses. * <p>May be overridden in subclasses in order to initialize further strategy objects. */
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
该方案初始化一系列与Spring MVC相关的策略组件:
- initMultipartResolver(context):
- 初始化
MultipartResolver
。这个组件用于处理文件上传请求。当用户需要上传文件时,Spring MVC会使用这个解析器来解析请求中的文件内容。
- 初始化
- initLocaleResolver(context):
- 初始化
LocaleResolver
。这个组件用于确定用户的区域设置(locale),例如语言、国家等。它可以帮助应用程序根据用户的区域设置来提供本地化的内容。
- 初始化
- initThemeResolver(context):
- 初始化
ThemeResolver
。这个组件用于确定用户的主题设置。它允许应用程序根据用户的偏好来更改应用的外观和风格。
- 初始化
- initHandlerMappings(context):
- 初始化
HandlerMapping
。这个组件用于将HTTP请求映射到相应的处理器(通常是控制器方法)。它根据请求的URL、HTTP方法和其他条件来确定应该调用哪个处理器。
- 初始化
- initHandlerAdapters(context):
- 初始化
HandlerAdapter
。这个组件用于调用处理器(控制器方法)。它根据处理器的类型(例如注解控制器、简单的控制器等)来选择合适的适配器来调用处理器。
- 初始化
- initHandlerExceptionResolvers(context):
- 初始化
HandlerExceptionResolver
。这个组件用于处理处理器(控制器方法)抛出的异常。它允许应用程序定义如何处理这些异常,例如返回特定的错误页面或错误消息。
- 初始化
- initRequestToViewNameTranslator(context):
- 初始化
RequestToViewNameTranslator
。这个组件用于根据请求信息生成视图名称。在某些情况下,你可能希望根据请求的某些属性(如URL的某部分)来动态确定要渲染的视图。
- 初始化
- initViewResolvers(context):
- 初始化
ViewResolver
。这个组件用于解析视图名称到具体的视图实现。例如,当控制器返回一个视图名称时,视图解析器会查找并返回与该名称匹配的视图(如JSP页面、Thymeleaf模板等)。
- 初始化
- initFlashMapManager(context):
- 初始化
FlashMapManager
。这个组件用于管理“flash”属性。Flash属性是在一个请求中设置,但在下一个请求中才可用的属性。它们通常用于在重定向之间传递数据。
- 初始化
其中比较重要的是initHandlerMappings(context)
、initHandlerAdapters(context)
、initHandlerExceptionResolvers(context)
。
在DispatcherServlet.properties
中定义了一些默认的配置,如下
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
他有默认的RequestMappingHandlerMapping、RequestMappingHandlerAdapter等
RequestMappingHandlerMapping
概述
使用SpringBoot进行web开发时,控制器类通常如下:
@RestController
@RequestMapping("user")
public class UserController {
/** * 用户注册 */
@PostMapping("/register")
public Result<Long> rgister(@RequestBody RegisterReq request) {
return Result.ok(userService.register(request));
}
}
一般控制器由@RestController注解修饰,通常@RestController注解与@RequestMapping配合使用,被修饰的类用于处理由DispatcherServlet分发下来的web请求。那么当一个web请求到达时,DispatcherServlet是如何将请求下发给对应的控制器处理呢。这就需要RequestMappingHandlerMapping
,顾名思义他就是用来处理@RequestMapping
注解的类。
RequestMappingHandlerMapping
就是接口HandlerMapping
的其中一个实现类。它允许开发者使用注解(如 @Controller
和 @RequestMapping
)来定义 URL 到处理器方法的映射,从而提供了一种更加声明式和类型安全的方式来配置请求映射。
其类图如下:
特点:
- RequestMappingHandlerMapping创建:DispatcherServlet初始化时被创建
- RequestMappingHandlerMapping初始化:实现了
InitilizingBean
,实现方法afterPropertiesSet
,在此方法里被初始化 - RequestMappingHandlerMapping初始化过程:获取容器中所有被@Controller注解或@RequestMapping注解修饰的类的对象(handler对象),然后遍历这些对象和其父对象的所有方法,将这些方法的@RequestMapping注解信息(如果有)解析成RequestMappingInfo,最后将handler对象,handler方法和RequestMappingInfo加入缓存并建立映射关系。
演示
我们还是用上一章的代码继续用。
在WebConfig中加入RequestMappingHandlerMapping
的bean
package com.cys.spring.chapter09;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@Configuration
@ComponentScan
public class WebConfig {
/** * 内嵌 web 容器工厂 * * @return */
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
/** * 内嵌 DispatcherServlet * * @return */
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
/** * 注册DispatcherServlet,Spring MVC的入口 * * @return */
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
// 第2个参数path表示
DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
registrationBean.setLoadOnStartup(1);
return registrationBean;
}
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
}
}
创建一个控制器类Controller1
package com.cys.spring.chapter09;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class Controller1 {
private static final Logger log = LoggerFactory.getLogger(Controller1.class);
@GetMapping("/test1")
public ModelAndView test1() {
log.info("test1()");
return null;
}
@PostMapping("/test2")
public ModelAndView test2(@RequestParam("name") String name) {
log.info("test2({})", name);
return null;
}
}
创建测试类
package com.cys.spring.chapter09;
import com.cys.spring.chapter09.WebConfig;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.Map;
public class TestHandlerMapping {
public static void main(String[] args) {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
// 解析@RequestMapping 以及派生注解,生成路径与控制器的映射关系,在初始化时生成
RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 获取其保存RequestMappingInfo与HandlerMethod的映射
Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
handlerMethods.forEach((k,v) -> {
System.out.println(k + "===>" + v);
});
}
}
这里我们通过getHandlerMethods
方法获取了RequestMappingHandlerMapping
的存储的映射信息,打印结果如下:
{GET [/test1]}===>com.cys.spring.chapter09.Controller1#test1()
{POST [/test2]}===>com.cys.spring.chapter09.Controller1#test2(String)
即结果就是哪个请求路径对应了哪个请求方法。
我们还可以具体查看下请求来了之后,使用的控制器方法,如下
// 请求来了,获取控制器方法,返回的对象为HandlerExecutionChain对象,即处理器执行链对象,他对HandlerMethod进行了包装,并配置了拦截器
// 可使用MockHttpServletRequest来模拟一个请求对象
HandlerExecutionChain chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/test1"));
System.out.println(chain);
RequestMappingHandlerAdapter
概述
RequestMappingHandlerAdapter
是一个请求处理器适配器,它的主要任务是:
- 解析请求参数:根据从
RequestMappingHandlerMapping
获得的处理器执行链中的信息,解析请求中的参数,并将它们绑定到控制器方法的参数上。 - 调用控制器方法:使用解析后的参数调用匹配的控制器方法。
- 处理返回值:控制器方法执行完毕后,
RequestMappingHandlerAdapter
负责处理方法的返回值。这可能包括将返回值转换为 HTTP 响应体、设置响应状态码和响应头,以及处理可能出现的异常。
在 Spring MVC 的请求处理流程中,DispatcherServlet
是核心组件,它负责调度整个请求处理过程。当请求到达时,DispatcherServlet
会首先委托 RequestMappingHandlerMapping
来查找匹配的请求映射。一旦找到匹配的映射,DispatcherServlet
就会使用 RequestMappingHandlerAdapter
来调用控制器方法并处理返回值。
因此,RequestMappingHandlerMapping
和 RequestMappingHandlerAdapter
之间是合作关系,它们共同确保请求能够正确地被路由到对应的控制器方法,并且控制器方法的返回值能够被正确地处理并返回给客户端。这种设计使得 Spring MVC 的请求处理过程更加灵活和可扩展,开发者可以轻松地添加自定义的请求映射和处理逻辑。
演示
如何调用方法
RequestMappingHandlerAdapter调用控制的方法就是通过invokeHandlerMethod
方法。
因为invokeHandlerMethod是受保护的方法,不方便我们后面演示,我们自己写一个,重写invokeHandlerMethod方法,改为public即可:
package com.cys.spring.chapter09;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyRequestMappingHandlerAdapter extends RequestMappingHandlerAdapter {
@Override
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
return super.invokeHandlerMethod(request, response, handlerMethod);
}
}
WebConfig
package com.cys.spring.chapter09;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@Configuration
@ComponentScan
public class WebConfig {
/** * 内嵌 web 容器工厂 * * @return */
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
/** * 内嵌 DispatcherServlet * * @return */
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
/** * 注册DispatcherServlet,Spring MVC的入口 * * @return */
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
// 第2个参数path表示
DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
registrationBean.setLoadOnStartup(1);
return registrationBean;
}
/** * 如果用DispatchServlet 初始化时默认添加的组件,并不会作为Bean * * @return */
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
}
/** * 加入RequestMappingHandlerAdapter,替换掉DispatchServlet 初始化时默认添加的 * 因为invokeHandlerMethod是收保护的方法,不方便我们后面演示,我们自己写一个,重写invokeHandlerMethod方法,改为public即可 * * @return */
@Bean
public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() {
return new MyRequestMappingHandlerAdapter();
}
}
测试:
package com.cys.spring.chapter09;
import com.cys.spring.chapter09.WebConfig;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.Map;
public class TestHandlerMapping {
public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
// 解析@RequestMapping 以及派生注解,生成路径与控制器的映射关系,在初始化时生成
RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 获取其保存RequestMappingInfo与HandlerMethod的映射
Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
handlerMethods.forEach((k,v) -> {
System.out.println(k + "===>" + v);
});
// 请求来了,获取控制器方法,返回的对象为HandlerExecutionChain对象,即处理器执行链对象,他对HandlerMethod进行了包装,并配置了拦截器
// 可使用MockHttpServletRequest来模拟一个请求对象
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test1");
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerExecutionChain chain = handlerMapping.getHandler(request);
System.out.println(chain);
// 获取MyRequestMappingHandlerAdapter
MyRequestMappingHandlerAdapter handlerAdapter = context.getBean(MyRequestMappingHandlerAdapter.class);
// 使用HandlerAdapter调用HandlerMethod,就是通过方法invokeHandlerMethod
handlerAdapter.invokeHandlerMethod(request, response, (HandlerMethod) chain.getHandler());
}
}
运行后控制台打印了:
15:38:31.536 [main] INFO com.cys.spring.chapter09.Controller1 - test1()
可以看到方法test1()被成功调用。
我们调用方法test2()试下:
package com.cys.spring.chapter09;
import com.cys.spring.chapter09.WebConfig;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.Map;
public class TestHandlerMapping {
public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
// 解析@RequestMapping 以及派生注解,生成路径与控制器的映射关系,在初始化时生成
RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 获取其保存RequestMappingInfo与HandlerMethod的映射
Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
handlerMethods.forEach((k,v) -> {
System.out.println(k + "===>" + v);
});
// 请求来了,获取控制器方法,返回的对象为HandlerExecutionChain对象,即处理器执行链对象,他对HandlerMethod进行了包装,并配置了拦截器
// 可使用MockHttpServletRequest来模拟一个请求对象
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/test2");
request.setParameter("name", "cys");
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerExecutionChain chain = handlerMapping.getHandler(request);
System.out.println(chain);
// 获取MyRequestMappingHandlerAdapter
MyRequestMappingHandlerAdapter handlerAdapter = context.getBean(MyRequestMappingHandlerAdapter.class);
// 使用HandlerAdapter调用HandlerMethod,就是通过方法invokeHandlerMethod
handlerAdapter.invokeHandlerMethod(request, response, (HandlerMethod) chain.getHandler());
}
}
结果如下:
15:42:37.238 [main] INFO com.cys.spring.chapter09.Controller1 - test2(cys)
如何解析参数
RequestMappingHandlerAdapter解析参数就是通过各种参数解析器。
上面我们看到,一个带参数的方法test2(),handlerAdapter也可以拿到参数并传给方法并正常调用。其中就是他内部维护可很多参数解析器。
package com.cys.spring.chapter09;
import com.cys.spring.chapter09.WebConfig;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.List;
import java.util.Map;
/** * @author Ethan * @date 2024/3/7 * @description */
public class TestHandlerMapping {
public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
// 解析@RequestMapping 以及派生注解,生成路径与控制器的映射关系,在初始化时生成
RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 获取其保存RequestMappingInfo与HandlerMethod的映射
Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
handlerMethods.forEach((k,v) -> {
System.out.println(k + "===>" + v);
});
// 请求来了,获取控制器方法,返回的对象为HandlerExecutionChain对象,即处理器执行链对象,他对HandlerMethod进行了包装,并配置了拦截器
// 可使用MockHttpServletRequest来模拟一个请求对象
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/test2");
request.setParameter("name", "cys");
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerExecutionChain chain = handlerMapping.getHandler(request);
System.out.println(chain);
// 获取MyRequestMappingHandlerAdapter
MyRequestMappingHandlerAdapter handlerAdapter = context.getBean(MyRequestMappingHandlerAdapter.class);
// 使用HandlerAdapter调用HandlerMethod,就是通过方法invokeHandlerMethod
handlerAdapter.invokeHandlerMethod(request, response, (HandlerMethod) chain.getHandler());
// 获取参数解析器
List<HandlerMethodArgumentResolver> argumentResolvers = handlerAdapter.getArgumentResolvers();
System.out.println("》》》打印所有的参数解析器");
argumentResolvers.forEach(ar -> {
System.out.println(ar);
});
}
}
打印出结果如下:
》》》打印所有的参数解析器
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver@367795c7
org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver@d2387c8
org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver@3956b302
org.springframework.web.servlet.mvc.method.annotation.PathVariableMapMethodArgumentResolver@1500e009
org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMethodArgumentResolver@1fd386c3
org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMapMethodArgumentResolver@edf4f36
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@29d334c
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor@100f9bbe
org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver@13e9f2e2
org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver@673bb956
org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver@cd7f1ae
org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver@60e949e1
org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver@3c4bc9fc
org.springframework.web.servlet.mvc.method.annotation.SessionAttributeMethodArgumentResolver@680362a
org.springframework.web.servlet.mvc.method.annotation.RequestAttributeMethodArgumentResolver@3569edd5
org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver@1f651cd8
org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver@7d0332e1
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@7a356a0d
org.springframework.web.servlet.mvc.method.annotation.RedirectAttributesMethodArgumentResolver@c827db
org.springframework.web.method.annotation.ModelMethodProcessor@377c68c6
org.springframework.web.method.annotation.MapMethodProcessor@538cd0f2
org.springframework.web.method.annotation.ErrorsMethodArgumentResolver@238ad8c
org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver@430fa4ef
org.springframework.web.servlet.mvc.method.annotation.UriComponentsBuilderMethodArgumentResolver@1761de10
org.springframework.web.servlet.mvc.method.annotation.PrincipalMethodArgumentResolver@22df874e
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver@654c1a54
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@5bdaf2ce
可以看到他有很多参数解析器,例如RequestParamMethodArgumentResolver
,就是来解析@RequestParam
注解标注的参数。具体每个参数解析器可以解析什么样的参数如下:
- RequestParamMethodArgumentResolver:处理方法参数上的
@RequestParam
注解,从请求参数中获取值。 - RequestParamMapMethodArgumentResolver:处理 Map 类型的参数,用于获取所有请求参数。
- PathVariableMethodArgumentResolver:处理方法参数上的
@PathVariable
注解,从 URI 模板变量中获取值。 - PathVariableMapMethodArgumentResolver:处理 Map 类型的参数,用于获取所有路径变量。
- MatrixVariableMethodArgumentResolver:处理 URI 矩阵变量,这些变量以分号(;)分隔并附加在路径段后面。
- MatrixVariableMapMethodArgumentResolver:处理 Map 类型的参数,用于获取所有矩阵变量。
- ServletModelAttributeMethodProcessor:处理
@ModelAttribute
注解的方法参数,通常用于绑定请求参数到模型对象。 - RequestResponseBodyMethodProcessor:处理带有
@RequestBody
或@ResponseBody
注解的方法参数或返回值,负责请求体的读取和响应体的写入。 - RequestPartMethodArgumentResolver:处理文件上传,特别是
@RequestPart
注解的方法参数。 - RequestHeaderMethodArgumentResolver:处理方法参数上的
@RequestHeader
注解,从请求头中获取值。 - RequestHeaderMapMethodArgumentResolver:处理 Map 类型的参数,用于获取所有请求头。
- ServletCookieValueMethodArgumentResolver:处理
@CookieValue
注解的方法参数,从 HTTP Cookie 中获取值。 - ExpressionValueMethodArgumentResolver:使用 Spring Expression Language (SpEL) 表达式处理方法参数。
- SessionAttributeMethodArgumentResolver:处理方法参数上的
@SessionAttribute
注解,从 HTTP session 中获取值。 - RequestAttributeMethodArgumentResolver:处理从请求属性中获取值的方法参数。
- ServletRequestMethodArgumentResolver 和 ServletResponseMethodArgumentResolver:分别处理 ServletRequest 和 ServletResponse 类型的方法参数。
- HttpEntityMethodProcessor:处理
@ResponseBody
注解的方法,当返回类型是HttpEntity
或ResponseEntity
时。 - RedirectAttributesMethodArgumentResolver:处理带有
@ModelAttribute
注解的RedirectAttributes
类型参数,用于在重定向时传递属性。 - ModelMethodProcessor 和 MapMethodProcessor:处理返回类型为 Model 或 Map 的方法,用于向模型中添加属性。
这些类和解析器/处理器协同工作,使得 Spring MVC 能够根据控制器方法上的注解自动处理请求参数和生成响应。它们大大简化了 Web 开发过程,使得开发者能够专注于业务逻辑的实现,而不是底层请求和响应的处理。
下面来看一个示例:
package com.cys.spring.chapter10;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockPart;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class TestArgumentResolver {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
// 准备测试 Request
HttpServletRequest request = mockRequest();
// 要点1. 控制器方法被封装为 HandlerMethod
HandlerMethod handlerMethod = new HandlerMethod(new Controller(), Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));
// 要点2. 准备对象绑定与类型转换
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
// 要点3. 准备 ModelAndViewContainer 用来存储中间 Model 结果
ModelAndViewContainer container = new ModelAndViewContainer();
// 要点4. 解析每个参数值
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
// 多个解析器组合
HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
composite.addResolvers(
// false 表示必须有 @RequestParam
new RequestParamMethodArgumentResolver(beanFactory, false),
new PathVariableMethodArgumentResolver(),
new RequestHeaderMethodArgumentResolver(beanFactory),
new ServletCookieValueMethodArgumentResolver(beanFactory),
new ExpressionValueMethodArgumentResolver(beanFactory),
new ServletRequestMethodArgumentResolver(),
new ServletModelAttributeMethodProcessor(false), // 必须有 @ModelAttribute
new RequestResponseBodyMethodProcessor(Collections.singletonList(new MappingJackson2HttpMessageConverter())),
new ServletModelAttributeMethodProcessor(true), // 省略了 @ModelAttribute
new RequestParamMethodArgumentResolver(beanFactory, true) // 省略 @RequestParam
);
String annotations = Arrays.stream(parameter.getParameterAnnotations()).map(a -> a.annotationType().getSimpleName()).collect(Collectors.joining());
String str = annotations.length() > 0 ? " @" + annotations + " " : " ";
parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
if (composite.supportsParameter(parameter)) {
// 支持此参数
Object v = composite.resolveArgument(parameter, container, new ServletWebRequest(request), factory);
// System.out.println(v.getClass());
System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName() + "->" + v);
System.out.println("模型数据为:" + container.getModel());
} else {
System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName());
}
}
/* 学到了什么 a. 每个参数处理器能干啥 1) 看是否支持某种参数 2) 获取参数的值 b. 组合模式在 Spring 中的体现 c. @RequestParam, @CookieValue 等注解中的参数名、默认值, 都可以写成活的, 即从 ${ } #{ }中获取 */
}
private static HttpServletRequest mockRequest() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("name1", "zhangsan");
request.setParameter("name2", "lisi");
request.addPart(new MockPart("file", "abc", "hello".getBytes(StandardCharsets.UTF_8)));
Map<String, String> map = new AntPathMatcher().extractUriTemplateVariables("/test/{id}", "/test/123");
System.out.println(map);
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, map);
request.setContentType("application/json");
request.setCookies(new Cookie("token", "123456"));
request.setParameter("name", "张三");
request.setParameter("age", "18");
request.setContent("{\"name\":\"李四\",\"age\":20}".getBytes(StandardCharsets.UTF_8));
return new StandardServletMultipartResolver().resolveMultipart(request);
}
static class Controller {
public void test(
@RequestParam("name1") String name1, // name1=张三
String name2, // name2=李四
@RequestParam("age") int age, // age=18
@RequestParam(name = "home", defaultValue = "${JAVA_HOME}") String home1, // 也可以从spring 获取数据,${JAVA_HOME}
@RequestParam("file") MultipartFile file, // 上传文件
@PathVariable("id") int id, // /test/124 /test/{id}
@RequestHeader("Content-Type") String header,
@CookieValue("token") String token,
@Value("${JAVA_HOME}") String home2, // spring 获取数据 ${} #{}
HttpServletRequest request, // request, response, session ...
@ModelAttribute("abc") User user1, // 解析对象,将 name=zhang&age=18
User user2, // name=zhang&age=18
@RequestBody User user3 // 解析json,转成对象
) {
}
}
static class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}
除此之外,我么那还可以自己创建一个参数解析器,来看一下解析过程。
首先创建一个参数注解
package com.cys.spring.chapter09;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Token {
}
然后创建解析器TokenArgumentResolver
package com.cys.spring.chapter09;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import java.lang.reflect.AnnotatedElement;
public class TokenArgumentResolver implements HandlerMethodArgumentResolver {
/** * 是否支持这个参数,即验证参数上是否有某个注解 * * @param parameter * @return */
@Override
public boolean supportsParameter(MethodParameter parameter) {
Token token = parameter.getParameterAnnotation(Token.class);
return token != null;
}
/** * 解析参数 * * @param parameter * @param mavContainer * @param webRequest * @param binderFactory * @return * @throws Exception */
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
// 可以从webRequest从拿到参数
return webRequest.getHeader("token");
}
}
修改WebConfig,将TokenArgumentResolver加到requestMappingHandlerAdapter
package com.cys.spring.chapter09;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.Collections;
import java.util.List;
@Configuration
@ComponentScan
public class WebConfig {
/** * 内嵌 web 容器工厂 * * @return */
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
/** * 内嵌 DispatcherServlet * * @return */
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
/** * 注册DispatcherServlet,Spring MVC的入口 * * @return */
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
// 第2个参数path表示
DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
registrationBean.setLoadOnStartup(1);
return registrationBean;
}
/** * 如果用DispatchServlet 初始化时默认添加的组件,并不会作为Bean * * @return */
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
}
/** * 加入RequestMappingHandlerAdapter,替换掉DispatchServlet 初始化时默认添加的 * 因为invokeHandlerMethod是收保护的方法,不方便我们后面演示,我们自己写一个,重写invokeHandlerMethod方法,改为public即可 * * @return */
@Bean
public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() {
TokenArgumentResolver tokenArgumentResolver = new TokenArgumentResolver();
MyRequestMappingHandlerAdapter myRequestMappingHandlerAdapter = new MyRequestMappingHandlerAdapter();
myRequestMappingHandlerAdapter.setCustomArgumentResolvers(Collections.singletonList(tokenArgumentResolver));
return myRequestMappingHandlerAdapter;
}
}
修改Controller1,添加方法test3,参数使用注解@Token
package com.cys.spring.chapter09;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class Controller1 {
private static final Logger log = LoggerFactory.getLogger(Controller1.class);
@GetMapping("/test1")
public ModelAndView test1() {
log.info("test1()");
return null;
}
@PostMapping("/test2")
public ModelAndView test2(@RequestParam("name") String name) {
log.info("test2({})", name);
return null;
}
@PutMapping("/test3")
public ModelAndView test3(@Token String token) {
log.info("test3({})", token);
return null;
}
}
修改测试类,调用test3
package com.cys.spring.chapter09;
import com.cys.spring.chapter09.WebConfig;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.List;
import java.util.Map;
public class TestHandlerMapping {
public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
// 解析@RequestMapping 以及派生注解,生成路径与控制器的映射关系,在初始化时生成
RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 获取其保存RequestMappingInfo与HandlerMethod的映射
Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
handlerMethods.forEach((k,v) -> {
System.out.println(k + "===>" + v);
});
// 请求来了,获取控制器方法,返回的对象为HandlerExecutionChain对象,即处理器执行链对象,他对HandlerMethod进行了包装,并配置了拦截器
// 可使用MockHttpServletRequest来模拟一个请求对象
MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/test3");
request.setParameter("name", "cys");
request.addHeader("token", "token test");
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerExecutionChain chain = handlerMapping.getHandler(request);
System.out.println(chain);
// 获取MyRequestMappingHandlerAdapter
MyRequestMappingHandlerAdapter handlerAdapter = context.getBean(MyRequestMappingHandlerAdapter.class);
// 使用HandlerAdapter调用HandlerMethod,就是通过方法invokeHandlerMethod
handlerAdapter.invokeHandlerMethod(request, response, (HandlerMethod) chain.getHandler());
}
}
最后打印结果:
17:21:24.514 [main] INFO com.cys.spring.chapter09.Controller1 - test3(token test)
可以看到成功解析参数token并调用test3。
如何解析返回值
返回值也有很多类型,需要解析才能正常返回,RequestMappingHandlerAdapter解析返回值就是通过返回值处理器。
演示:
package com.cys.spring.chapter09;
import com.cys.spring.chapter09.WebConfig;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.List;
import java.util.Map;
public class TestHandlerMapping {
public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
// 解析@RequestMapping 以及派生注解,生成路径与控制器的映射关系,在初始化时生成
RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 获取其保存RequestMappingInfo与HandlerMethod的映射
Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
handlerMethods.forEach((k,v) -> {
System.out.println(k + "===>" + v);
});
// 请求来了,获取控制器方法,返回的对象为HandlerExecutionChain对象,即处理器执行链对象,他对HandlerMethod进行了包装,并配置了拦截器
// 可使用MockHttpServletRequest来模拟一个请求对象
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/test2");
request.setParameter("name", "cys");
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerExecutionChain chain = handlerMapping.getHandler(request);
System.out.println(chain);
// 获取MyRequestMappingHandlerAdapter
MyRequestMappingHandlerAdapter handlerAdapter = context.getBean(MyRequestMappingHandlerAdapter.class);
// 使用HandlerAdapter调用HandlerMethod,就是通过方法invokeHandlerMethod
handlerAdapter.invokeHandlerMethod(request, response, (HandlerMethod) chain.getHandler());
// 获取参数解析器
List<HandlerMethodArgumentResolver> argumentResolvers = handlerAdapter.getArgumentResolvers();
System.out.println("》》》打印所有的参数解析器");
argumentResolvers.forEach(ar -> {
System.out.println(ar);
});
// 获取返回值解析器
List<HandlerMethodReturnValueHandler> returnValueHandlers = handlerAdapter.getReturnValueHandlers();
System.out.println("》》》打印所有的返回值处理器");
returnValueHandlers.forEach(rh -> {
System.out.println(rh);
});
}
}
打印结果如下:
》》》打印所有的返回值处理器
org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler@1ce93c18
org.springframework.web.method.annotation.ModelMethodProcessor@19f21b6b
org.springframework.web.servlet.mvc.method.annotation.ViewMethodReturnValueHandler@1532c619
org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler@46044faa
org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler@1358b28e
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@1a78dacd
org.springframework.web.servlet.mvc.method.annotation.HttpHeadersReturnValueHandler@19f9d595
org.springframework.web.servlet.mvc.method.annotation.CallableMethodReturnValueHandler@7de4a01f
org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler@2bfeb1ef
org.springframework.web.servlet.mvc.method.annotation.AsyncTaskMethodReturnValueHandler@778ca8ef
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@208e9ef6
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor@78b236a0
org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler@261d8190
org.springframework.web.method.annotation.MapMethodProcessor@34448e6c
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@60e9df3c
其中ModelAndViewMethodReturnValueHandler
就是来处理ModelAndView
返回值类型的。
当然我们也可以自定义返回值处理器,这里不再演示。
文章评论