【Spring高级】RequestMappingHandlerMapping和RequestMappingHandlerAdapter

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相关的策略组件:

  1. initMultipartResolver(context):
    • 初始化MultipartResolver。这个组件用于处理文件上传请求。当用户需要上传文件时,Spring MVC会使用这个解析器来解析请求中的文件内容。
  2. initLocaleResolver(context):
    • 初始化LocaleResolver。这个组件用于确定用户的区域设置(locale),例如语言、国家等。它可以帮助应用程序根据用户的区域设置来提供本地化的内容。
  3. initThemeResolver(context):
    • 初始化ThemeResolver。这个组件用于确定用户的主题设置。它允许应用程序根据用户的偏好来更改应用的外观和风格。
  4. initHandlerMappings(context):
    • 初始化HandlerMapping。这个组件用于将HTTP请求映射到相应的处理器(通常是控制器方法)。它根据请求的URL、HTTP方法和其他条件来确定应该调用哪个处理器。
  5. initHandlerAdapters(context):
    • 初始化HandlerAdapter。这个组件用于调用处理器(控制器方法)。它根据处理器的类型(例如注解控制器、简单的控制器等)来选择合适的适配器来调用处理器。
  6. initHandlerExceptionResolvers(context):
    • 初始化HandlerExceptionResolver。这个组件用于处理处理器(控制器方法)抛出的异常。它允许应用程序定义如何处理这些异常,例如返回特定的错误页面或错误消息。
  7. initRequestToViewNameTranslator(context):
    • 初始化RequestToViewNameTranslator。这个组件用于根据请求信息生成视图名称。在某些情况下,你可能希望根据请求的某些属性(如URL的某部分)来动态确定要渲染的视图。
  8. initViewResolvers(context):
    • 初始化ViewResolver。这个组件用于解析视图名称到具体的视图实现。例如,当控制器返回一个视图名称时,视图解析器会查找并返回与该名称匹配的视图(如JSP页面、Thymeleaf模板等)。
  9. 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 是一个请求处理器适配器,它的主要任务是:

  1. 解析请求参数:根据从 RequestMappingHandlerMapping 获得的处理器执行链中的信息,解析请求中的参数,并将它们绑定到控制器方法的参数上。
  2. 调用控制器方法:使用解析后的参数调用匹配的控制器方法。
  3. 处理返回值:控制器方法执行完毕后,RequestMappingHandlerAdapter 负责处理方法的返回值。这可能包括将返回值转换为 HTTP 响应体、设置响应状态码和响应头,以及处理可能出现的异常。

在 Spring MVC 的请求处理流程中,DispatcherServlet 是核心组件,它负责调度整个请求处理过程。当请求到达时,DispatcherServlet 会首先委托 RequestMappingHandlerMapping 来查找匹配的请求映射。一旦找到匹配的映射,DispatcherServlet 就会使用 RequestMappingHandlerAdapter 来调用控制器方法并处理返回值。

因此,RequestMappingHandlerMappingRequestMappingHandlerAdapter 之间是合作关系,它们共同确保请求能够正确地被路由到对应的控制器方法,并且控制器方法的返回值能够被正确地处理并返回给客户端。这种设计使得 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注解标注的参数。具体每个参数解析器可以解析什么样的参数如下:

  1. RequestParamMethodArgumentResolver:处理方法参数上的 @RequestParam 注解,从请求参数中获取值。
  2. RequestParamMapMethodArgumentResolver:处理 Map 类型的参数,用于获取所有请求参数。
  3. PathVariableMethodArgumentResolver:处理方法参数上的 @PathVariable 注解,从 URI 模板变量中获取值。
  4. PathVariableMapMethodArgumentResolver:处理 Map 类型的参数,用于获取所有路径变量。
  5. MatrixVariableMethodArgumentResolver:处理 URI 矩阵变量,这些变量以分号(;)分隔并附加在路径段后面。
  6. MatrixVariableMapMethodArgumentResolver:处理 Map 类型的参数,用于获取所有矩阵变量。
  7. ServletModelAttributeMethodProcessor:处理 @ModelAttribute 注解的方法参数,通常用于绑定请求参数到模型对象。
  8. RequestResponseBodyMethodProcessor:处理带有 @RequestBody@ResponseBody 注解的方法参数或返回值,负责请求体的读取和响应体的写入。
  9. RequestPartMethodArgumentResolver:处理文件上传,特别是 @RequestPart 注解的方法参数。
  10. RequestHeaderMethodArgumentResolver:处理方法参数上的 @RequestHeader 注解,从请求头中获取值。
  11. RequestHeaderMapMethodArgumentResolver:处理 Map 类型的参数,用于获取所有请求头。
  12. ServletCookieValueMethodArgumentResolver:处理 @CookieValue 注解的方法参数,从 HTTP Cookie 中获取值。
  13. ExpressionValueMethodArgumentResolver:使用 Spring Expression Language (SpEL) 表达式处理方法参数。
  14. SessionAttributeMethodArgumentResolver:处理方法参数上的 @SessionAttribute 注解,从 HTTP session 中获取值。
  15. RequestAttributeMethodArgumentResolver:处理从请求属性中获取值的方法参数。
  16. ServletRequestMethodArgumentResolverServletResponseMethodArgumentResolver:分别处理 ServletRequest 和 ServletResponse 类型的方法参数。
  17. HttpEntityMethodProcessor:处理 @ResponseBody 注解的方法,当返回类型是 HttpEntityResponseEntity 时。
  18. RedirectAttributesMethodArgumentResolver:处理带有 @ModelAttribute 注解的 RedirectAttributes 类型参数,用于在重定向时传递属性。
  19. ModelMethodProcessorMapMethodProcessor:处理返回类型为 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返回值类型的。

当然我们也可以自定义返回值处理器,这里不再演示。

  • 29
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ethan-running

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值