SpringMVC九大组件之二RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter是如何配合使用的

RequestMappingHandlerMapping 的作用收集路径映射和对应的方法;

RequestMappingHandlerAdapter的作用参数解析以及结果返回

DispatcherServlet初始化的时候会执行一下代码一起初始化九大组件

	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	/**
	 * 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);
	}

RequestMappingHandlerMapping初始化出来以后就会

1,先找到有@Controller或者@RestController的类

@org.springframework.stereotype.Controller

public class Controller {
    @GetMapping("/test1")
    public ModelAndView test1(){
        System.out.println("test1");
        return null;
  }
}

2,找到方法上加了各种@Mapping的方法把他们筛选处理

    @GetMapping("/test1")
    public ModelAndView test1(){
        System.out.println("test1");
        return null;
    }
    @RequestMapping("/test2")
    public ModelAndView test2(){
        System.out.println("test2");
        return null;
    }
    @PostMapping ("/test3")
    public ModelAndView test3(@Token String token){
        System.out.println(token);
        return null;
    }

3,记录每个方法路径和方法的对应方法关系把他们存储在Map表中也就是存储在RequestMappingHandlerMapping

 RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();

4,RequestMappingHandlerMapping 初始化时,会收集所有 @RequestMapping 映射信息,封装为 Map,其中

  • key 是 RequestMappingInfo 类型,包括请求路径、请求方法等信息

可以看出上面一段代码Map的2个指定类型其中一个是RequestMappingInfo对象

里面包含了具体路径和请求方式等等

  • value 是 HandlerMethod 类型,包括控制器方法对象、控制器对象

可以看出上面一段代码Map的2个指定类型其中一个是HandlerMethod对象

  • 有了这个 Map,就可以在请求到达时,快速完成映射,找到 HandlerMethod 并与匹配的拦截器一起返回给 DispatcherServlet

调用请求的时候,也就是用户发送请求的时候

会首先经过DispatcherServlet 的转发把请求转发给RequestMappingHandlerMapping

RequestMappingHandlerMapping拿着映射请求的路径去他的Map表里面找对应的方法

并且把他和拦截器封装成一个执行链

RequestMappingHandlerMappinghandlerMapping=context.getBean(RequestMappingHandlerMapping.class);
HandlerExecutionChain chain = handlerMapping.getHandler(request);

只会会把包装好的执行链交给RequestMappingHandlerAdapter去执行

一下代码模拟浏览器发送请求,可以看出RequestMappingHandlerAdapter执行的方法是invokeHandlerMethod里面传进去的产生是一个HandlerMethod

     //request
        MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test4");
        //response
        MockHttpServletResponse response = new MockHttpServletResponse();
        //方法执行链
        HandlerExecutionChain chain = handlerMapping.getHandler(request);
        MyRequestMappingHandlerAdapter handlerAdapter = (MyRequestMappingHandlerAdapter) context.getBean(RequestMappingHandlerAdapter.class);
        //如果需要视图分辨率,则调用准备ModelAndView的RequestMapping处理程序方法
        ModelAndView modelAndView = handlerAdapter.invokeHandlerMethod(request, response, (HandlerMethod) chain.getHandler());

在RequestMappingHandlerAdapter里做参数解析HandlerMethodArgumentResolver和

返回值处理HandlerMethodReturnValueHandler

在这里面我们可以加入自己的逻辑

可以自己定义参数解析器

自定义参数解析器注册以及使用

定义注解类

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Token {
}

定义控制器

public class TokenArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(Token.class);
    }
​
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        String token = webRequest.getHeader("token");
        //解析逻辑
        //解析出来以后直接返回
        return token;
    }
}

注册解析器到RequestMappingHandlerAdapte

    @Bean
    public MyRequestMappingHandlerAdapter requestMappingHandlerMapping(){
        MyRequestMappingHandlerAdapter myRequestMappingHandlerAdapter = new MyRequestMappingHandlerAdapter();
        TokenArgumentResolver tokenArgumentResolver=new TokenArgumentResolver();
        //为自定义参数类型提供解析器。自定义解析器在内置解析器之后排序。要覆盖对参数解析的内置支持,请改用setArgumentResolvers 。
        List<HandlerMethodArgumentResolver> list = new ArrayList<>();
        list.add(tokenArgumentResolver);
        myRequestMappingHandlerAdapter.setCustomArgumentResolvers(list);
        return myRequestMappingHandlerAdapter;
    }

使用

    @PostMapping ("/test3")
    public ModelAndView test3(@Token String token){
        System.out.println(token);
        return null;
    }

测试

     //作用解析@RequestMapping以及派生注解,生成路径与控制器方法的映射关系,在初始化时就生成
        RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
        //request
        MockHttpServletRequest request = new MockHttpServletRequest("POST", "/test3");
        request.addHeader("token","我是自定义token解析的");
        //response
        MockHttpServletResponse response = new MockHttpServletResponse();
        //方法执行链
        HandlerExecutionChain chain = handlerMapping.getHandler(request);
        MyRequestMappingHandlerAdapter handlerAdapter = (MyRequestMappingHandlerAdapter) context.getBean(RequestMappingHandlerAdapter.class);

可以自定义返回结果

自定义返回值处理器

注解

@Target(value = ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Yml {
}

控制器

public class YmlHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return returnType.hasMethodAnnotation(Yml.class);
    }
​
    @Override
    //returnValue 返回值
    public void handleReturnValue(Object returnValue,
                                  MethodParameter returnType,
                                  ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest) throws Exception {
​
        //转换返回结果给Yml
        String str = new Yaml().dump(returnType);
        //写入响应体
        HttpServletResponse nativeResponse = webRequest.getNativeResponse(HttpServletResponse.class);
        nativeResponse.setContentType("text/plain; charset=UTF-8");
        nativeResponse.getWriter().print(str);
​
        //设置请求已经处理完毕
        mavContainer.setRequestHandled(true);
    }
}

注册控制器

    @Bean
    public MyRequestMappingHandlerAdapter requestMappingHandlerMapping(){
        MyRequestMappingHandlerAdapter myRequestMappingHandlerAdapter = new MyRequestMappingHandlerAdapter();
        
        myRequestMappingHandlerAdapter.setCustomArgumentResolvers(list);
        //添加自定义参数解析器
        YmlHandlerMethodReturnValueHandler ymlHandlerMethodReturnValueHandler=new YmlHandlerMethodReturnValueHandler();
        List<HandlerMethodReturnValueHandler> returnValueHandlers=new ArrayList<>();
        returnValueHandlers.add(ymlHandlerMethodReturnValueHandler);
        myRequestMappingHandlerAdapter.setCustomReturnValueHandlers(returnValueHandlers);
        return myRequestMappingHandlerAdapter;
    }

测试

    
    //作用解析@RequestMapping以及派生注解,生成路径与控制器方法的映射关系,在初始化时就生成
        RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
        //request
        MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test4");
        //response
        MockHttpServletResponse response = new MockHttpServletResponse();
        //方法执行链
        HandlerExecutionChain chain = handlerMapping.getHandler(request);
        MyRequestMappingHandlerAdapter handlerAdapter = (MyRequestMappingHandlerAdapter) context.getBean(RequestMappingHandlerAdapter.class);
        //如果需要视图分辨率,则调用准备ModelAndView的RequestMapping处理程序方法
        ModelAndView modelAndView = handlerAdapter.invokeHandlerMethod(request, response, (HandlerMethod) chain.getHandler());
​
        //获取相应内容
        byte[] contentAsByteArray = response.getContentAsByteArray();
​
        System.out.println(new String(contentAsByteArray, StandardCharsets.UTF_8));

可以看以下拓展篇,RequestMappingHandlerAdapter内部参数是如何解析的

SpringMVC九大组件之一RequestMappingHandlerAdapter内部参数解析器如何解析@RequestParam

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值