spring 24 控制器方法执行流程
图1
classDiagram
class ServletInvocableHandlerMethod {
+invokeAndHandle(ServletWebRequest,ModelAndViewContainer)
}
HandlerMethod <|-- ServletInvocableHandlerMethod
HandlerMethod o-- bean
HandlerMethod o-- method
ServletInvocableHandlerMethod o-- WebDataBinderFactory
ServletInvocableHandlerMethod o-- ParameterNameDiscoverer
ServletInvocableHandlerMethod o-- HandlerMethodArgumentResolverComposite
ServletInvocableHandlerMethod o-- HandlerMethodReturnValueHandlerComposite
HandlerMethod 需要
- bean 即是哪个 Controller
- method 即是 Controller 中的哪个方法
ServletInvocableHandlerMethod 需要
- WebDataBinderFactory 负责对象绑定、类型转换
- ParameterNameDiscoverer 负责参数名解析
- HandlerMethodArgumentResolverComposite 负责解析参数
- HandlerMethodReturnValueHandlerComposite 负责处理返回值
点击查看代码
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(WebConfig.class);
//为了获得一些资源添加到容器中
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
adapter.setApplicationContext(context);
adapter.afterPropertiesSet();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("name", "张三");
/*
现在可以通过 ServletInvocableHandlerMethod 把这些整合在一起, 并完成控制器方法的调用, 如下
*/
ServletInvocableHandlerMethod handlerMethod = new ServletInvocableHandlerMethod(
new Controller1(), Controller1.class.getMethod("foo", User.class));
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
//绑定工厂, @InitBinder 负责对象绑定、类型转换
handlerMethod.setDataBinderFactory(factory);
//参数名解析
handlerMethod.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());
//参数解析
handlerMethod.setHandlerMethodArgumentResolvers(getArgumentResolvers(context));
ModelAndViewContainer container = new ModelAndViewContainer();
// 获取模型工厂方法
Method getModelFactory = RequestMappingHandlerAdapter.class.getDeclaredMethod("getModelFactory", HandlerMethod.class, WebDataBinderFactory.class);
getModelFactory.setAccessible(true);
ModelFactory modelFactory = (ModelFactory) getModelFactory.invoke(adapter, handlerMethod, factory);
// 初始化模型数据 查找加了注解 @ModelAttribute 的控制器方法,或者配置类中加了 @ControllerAdvice 的 @ModelAttribute ,利用反射判断。
modelFactory.initModel(new ServletWebRequest(request), container, handlerMethod);
//调用控制器方法
handlerMethod.invokeAndHandle(new ServletWebRequest(request), container);
//获得存储在 container 中的模型数据
System.out.println(container.getModel());
context.close();
@Configuration
public class WebConfig {
@ControllerAdvice // @ModelAtribute 加在 @ControllerAdvice 上面,全局设置,所有的控制器方法都要满足。
static class MyControllerAdvice {
@ModelAttribute("a") //将方法返回值作为模型数据加入 container 中,没设置名字,默认返回值类型首字母小写。
public String aa() {
return "aa";
}
}
@Controller
static class Controller1 {
@ModelAttribute("b") //将方法返回值作为模型数据加入 container 中,没设置名字,默认返回值类型首字母小写。
public String aa() {
return "bb";
}
@ResponseStatus(HttpStatus.OK) //方法上加了可以跳过返回值解析过程,说明响应已经设置完成,可以返回了。
public ModelAndView foo(@ModelAttribute("u") User user) { //将传入的参数作为模型数据加入 container 中,没设置名字,默认参数类型首字母小写。
System.out.println("foo");
return null;
}
}