目录
流程图
先分3个部分来看下整体的流程图
图1
ServletInvocableHandlerMethod
是 Spring Framework 中对HandlerMethod
的实现。
ServletInvocableHandlerMethod的整体架构图
HandlerMethod 需要
- bean, 即是哪个 Controller
- method ,即是 Controller 中的哪个方法
ServletInvocableHandlerMethod 需要
- WebDataBinderFactory: 负责对象绑定、类型转换
- ParameterNameDiscoverer: 负责参数名解析
- HandlerMethodArgumentResolverComposite: 负责解析参数
- HandlerMethodReturnValueHandlerComposite :负责处理返回值
ServletInvocableHandlerMethod实现了HandlerMethod并扩展了以上功能。
图2
下图是RequestMappingHandlerAdapter工作的一些准备工作:
- 初始化 advice @InitBinder,添加一些自定义类型转换器
- 初始化advice @ModelAttribute,生产模型数据
- 将第2步的模型数据添加到ModelAndViewContainer
图3
RequestMappingHandlerAdapter的执行流程,主要做了三件事
- 解析参数
- 反射调用方法
- 处理返回值
演示
首先创建WebConfig
package com.cys.spring.chapter13;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;
@Configuration
public class WebConfig {
@Controller
static class Controller1 {
// ResponseStatus是为了测试方便,可以先不加返回值处理器
@ResponseStatus(HttpStatus.OK)
public ModelAndView foo(User user) { // 省略了@ModelAttribute
System.out.println("foo");
return null;
}
}
static class User {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
}
测试类:
package com.cys.spring.chapter13;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.context.request.ServletWebRequest;
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.servlet.mvc.method.annotation.*;
import java.util.Collections;
public class TestHandlerAdapter {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(WebConfig.class);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("name", "张三");
/*
现在可以通过 ServletInvocableHandlerMethod 把这些整合在一起, 并完成控制器方法的调用, 如下
*/
// 创建HandlerMethod
ServletInvocableHandlerMethod handlerMethod = new ServletInvocableHandlerMethod(
new WebConfig.Controller1(), WebConfig.Controller1.class.getMethod("foo", WebConfig.User.class));
// 创建DataBinderFactory,即类型转换器工厂
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
// 给handlerMethod设置需要的类型转换器、参数解析器等
handlerMethod.setDataBinderFactory(factory);
handlerMethod.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer()); // 解析参数名
handlerMethod.setHandlerMethodArgumentResolvers(getArgumentResolvers(context)); // 参数解析器
// 创建ModelAndView容器,用于存储模型对象
ModelAndViewContainer container = new ModelAndViewContainer();
// 反射调用方法
handlerMethod.invokeAndHandle(new ServletWebRequest(request), container);
System.out.println(container.getModel());
context.close();
}
/**
* 获取一个参数解析器组合器,解析常见的参数
*
* @param context
* @return
*/
public static HandlerMethodArgumentResolverComposite getArgumentResolvers(AnnotationConfigApplicationContext context) {
HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
composite.addResolvers(
new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), false),
new PathVariableMethodArgumentResolver(),
new RequestHeaderMethodArgumentResolver(context.getDefaultListableBeanFactory()),
new ServletCookieValueMethodArgumentResolver(context.getDefaultListableBeanFactory()),
new ExpressionValueMethodArgumentResolver(context.getDefaultListableBeanFactory()),
new ServletRequestMethodArgumentResolver(),
new ServletModelAttributeMethodProcessor(false),
new RequestResponseBodyMethodProcessor(Collections.singletonList(new MappingJackson2HttpMessageConverter())),
new ServletModelAttributeMethodProcessor(true),
new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), true)
);
return composite;
}
}
运行结果如下:
foo
{u=User{name='张三'}, org.springframework.validation.BindingResult.u=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
可以看到foo方法成功被调用。并且ModelAndViewContainer
含有一个mode,打印的{u=User{name='张三'}
,只有被@ModelAttribute
或省略了此注解的参数才回呗放到ModelAndViewContainer中。