写点什么

SpringMVC 源码分析 -HandlerAdapter(3)-RequestMappingHandlerAdapter 请求处理

用户头像
Brave
关注
发布于: 4 小时前

RequestMappingHanderAdapter 的请求处理


RequestMappingHanderAdapter 处理请求的入口方法是 handleInternal:

@Overrideprotected ModelAndView handleInternal(HttpServletRequest request,    HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 最终返回类型 ModelAndView mav; // 检查请求类型和session支持 checkRequest(request);
// 执行请求的处理 if (this.synchronizeOnSession) {//是否对Session同步 HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No HttpSession available -> no mutex necessary mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No synchronization on session demanded at all... mav = invokeHandlerMethod(request, response, handlerMethod); }
// 给缓存设置过期时间 if (!response.containsHeader(HEADER_CACHE_CONTROL)) { // 检查处理器是否有@SessionAttributes注解 if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } }
return mav;}
复制代码


可以看到,处理的请求最终会返回一个 ModelAndView 对象;


checkRequest 方法


checkRequest 方法源码:

protected final void checkRequest(HttpServletRequest request) throws ServletException {    // 根据supportedMethods属性判断是否支持当前请求的请求类型  String method = request.getMethod();  if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {    throw new HttpRequestMethodNotSupportedException(        method, StringUtils.toStringArray(this.supportedMethods));  }
// 当requireSession(默认false)为true时,检查Session是否存在 if (this.requireSession && request.getSession(false) == null) { throw new HttpSessionRequiredException("Pre-existing session required but none found"); } }
复制代码


根据 supportedMethods 属性判断是否支持当前的请求类型

那么,supportedMethods 属性是如何初始化的?

private Set<String> supportedMethods;
复制代码


supportedMethods 属性默认为空,可以在 RequestMappingHandlerAdapter 初始化时进行设置;

public WebContentGenerator(boolean restrictDefaultSupportedMethods) {  if (restrictDefaultSupportedMethods) {    this.supportedMethods = new LinkedHashSet<String>(4);    this.supportedMethods.add(METHOD_GET);    this.supportedMethods.add(METHOD_HEAD);    this.supportedMethods.add(METHOD_POST);  }  initAllowHeader();}
复制代码

可以看到,如果在构造方法中为 restrictDefaultSupportedMethods 传入 true,

那样,supportedMethods 就仅能够支持 GET,HEAD,POST 三种请求;


public AbstractHandlerMethodAdapter() {  super(false);}
复制代码

AbstractHandlerMethodAdapter 继承自 WebContentGenerator,

在 RequestMappingHandlerAdapter 的父类 AbstractHandlerMothodAdapter 中,

直接传入 false,默认不检查请求类型;


getSessionAttributesHandler、getSessionAttributesHandler 方法源码

private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) {  Class<?> handlerType = handlerMethod.getBeanType();  SessionAttributesHandler sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType);  if (sessionAttrHandler == null) {    synchronized (this.sessionAttributesHandlerCache) {      sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType);      if (sessionAttrHandler == null) {        sessionAttrHandler = new SessionAttributesHandler(handlerType, sessionAttributeStore);        this.sessionAttributesHandlerCache.put(handlerType, sessionAttrHandler);      }    }  }  return sessionAttrHandler;}
// 是否存在attributeNames或attributeTypespublic boolean hasSessionAttributes() { return (this.attributeNames.size() > 0 || this.attributeTypes.size() > 0);}
复制代码


sessionAttributesHandlerCache 属性是一个 new 好的空集合:

private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache = new ConcurrentHashMap<Class<?>, SessionAttributesHandler>(64);
复制代码


首先,根据 handlerType 到 SessionAttributesHandler 中查找 SessionAttributesHandler,

如果没有,就创建出来添加进去;

这里使用了一个双重校验锁,并且使用了一种懒汉式的加载方式;


这里还用到了一个 sessionAttributeStore 属性;


sessionAttributeStore 属性

private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
复制代码


DefaultSessionAttributeStore 实现了 SessionAttributeStore 接口:

public class DefaultSessionAttributeStore implements SessionAttributeStore {    private String attributeNamePrefix = "";
public DefaultSessionAttributeStore() { }
public void setAttributeNamePrefix(String attributeNamePrefix) { this.attributeNamePrefix = attributeNamePrefix != null?attributeNamePrefix:""; }
public void storeAttribute(WebRequest request, String attributeName, Object attributeValue) { Assert.notNull(request, "WebRequest must not be null"); Assert.notNull(attributeName, "Attribute name must not be null"); Assert.notNull(attributeValue, "Attribute value must not be null"); String storeAttributeName = this.getAttributeNameInSession(request, attributeName); request.setAttribute(storeAttributeName, attributeValue, 1); }
public Object retrieveAttribute(WebRequest request, String attributeName) { Assert.notNull(request, "WebRequest must not be null"); Assert.notNull(attributeName, "Attribute name must not be null"); String storeAttributeName = this.getAttributeNameInSession(request, attributeName); return request.getAttribute(storeAttributeName, 1); }
public void cleanupAttribute(WebRequest request, String attributeName) { Assert.notNull(request, "WebRequest must not be null"); Assert.notNull(attributeName, "Attribute name must not be null"); String storeAttributeName = this.getAttributeNameInSession(request, attributeName); request.removeAttribute(storeAttributeName, 1); }
protected String getAttributeNameInSession(WebRequest request, String attributeName) { return this.attributeNamePrefix + attributeName; }}
复制代码


从代码可以看到,SessionAttributeStore 属性并不是用来保存 SessionAttribute 参数的容器,

而是保存 SessionAttribute 的工具,SessionAttribute 最终被保存在了 request 中;


设置缓存的过期时间

if (!response.containsHeader(HEADER_CACHE_CONTROL)) {  // 如果包含SessionAttributes注解,且有name或type属性  if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {    // 设置缓存时间,cacheSecondsForSessionAttributeHandlers默认0    applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);  }  else {    prepareResponse(response);  }}
// cacheSeconds>0设置缓存时间,cacheSeconds=0阻止缓存protected final void applyCacheSeconds(HttpServletResponse response, int cacheSeconds) { if (this.useExpiresHeader || !this.useCacheControlHeader) { // Deprecated HTTP 1.0 cache behavior, as in previous Spring versions if (cacheSeconds > 0) { cacheForSeconds(response, cacheSeconds); } else if (cacheSeconds == 0) { preventCaching(response); } } else { CacheControl cControl; if (cacheSeconds > 0) { cControl = CacheControl.maxAge(cacheSeconds, TimeUnit.SECONDS); if (this.alwaysMustRevalidate) { cControl = cControl.mustRevalidate(); } } else if (cacheSeconds == 0) { cControl = (this.useCacheControlNoStore ? CacheControl.noStore() : CacheControl.noCache()); } else { cControl = CacheControl.empty(); } applyCacheControl(response, cControl); }}
复制代码



invokeHandlerMethod


invokeHandlerMethod 是一个核心方法,它具体执行请求的处理


invokeHandlerMethod 源码:

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,    HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 使用request和response创建ServletWebRequest类型的webRequest ServletWebRequest webRequest = new ServletWebRequest(request, response); try { // 初始化三个变量 WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); invocableMethod.setDataBinderFactory(binderFactory); invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// 创建ModelAndViewContainer,用于保存Model和View ModelAndViewContainer mavContainer = new ModelAndViewContainer(); mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); modelFactory.initModel(webRequest, mavContainer, invocableMethod); mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); // 异步处理相关... AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.setTaskExecutor(this.taskExecutor); asyncManager.setAsyncWebRequest(asyncWebRequest); asyncManager.registerCallableInterceptors(this.callableInterceptors); asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) { Object result = asyncManager.getConcurrentResult(); mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; asyncManager.clearConcurrentResult(); if (logger.isDebugEnabled()) { logger.debug("Found concurrent result value [" + result + "]"); } invocableMethod = invocableMethod.wrapConcurrentResult(result); } // 处理请求 invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; }
return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); }}
复制代码

首先,使用 request 和 response 创建 ServletWebRequest 类型的 webRequest;

备注:在 ArgumentResolve 解析参数时,使用的 request 就是这个 webRequest;

然后,初始化三个变量 WebDataBinderFactory、ModelFactory、ServletInvocableHandlerMethod;

之后,新建传递参数的 ModelAndViewContainer 容器,执行请求、请求执行完成的后置处理;



WebDataBinderFactory 初始化


WebDataBinderFactory:用于创建 WebDataBinder

WebDataBinder:用于参数绑定,实现参数和 String 之间的类型转换;


备注:

  • 在 ArgumentResolve 进行参数解析的过程中,会用到 WebDataBinder;

  • ModelFactory 在更新 Model 时,也会用到 WebDataBinder;


getDataBinderFactory 源码:

private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {  // 检查当前Handler中的InitBinder方法是否已经在缓存中  Class<?> handlerType = handlerMethod.getBeanType();  Set<Method> methods = this.initBinderCache.get(handlerType);  // 如果没在缓存中,找到并设置到缓存  if (methods == null) {    methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);    this.initBinderCache.put(handlerType, methods);  }  // 保存initBinder方法的集合  List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();  // 将所有符合条件的全局InitBinder方法添加到initBinderMethods  for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache.entrySet()) {    if (entry.getKey().isApplicableToBeanType(handlerType)) {      Object bean = entry.getKey().resolveBean();      for (Method method : entry.getValue()) {        initBinderMethods.add(createInitBinderMethod(bean, method));      }    }  }  // 将当前Handler中的InitBinder方法添加到initBinderMethods  for (Method method : methods) {    Object bean = handlerMethod.getBean();    initBinderMethods.add(createInitBinderMethod(bean, method));  }  // 创建DataBinderFactory并返回,DataBinderFactory和InitBinder方法相关联  return createDataBinderFactory(initBinderMethods);}
protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods) throws Exception {
return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());}
复制代码

WebDataBinderFactory 的创建过程:

  • 找出符合条件的 @InitBinder 方法;

  • 使用它们创建 ServletRequestDataBinderFactory 类型的 WebDataBinderFactory;


InitBinder 方法包含两部分:

  1. 注释了 @ControllerAdvice 且符合要求的全局处理器中的 InitBinder 方法;

(创建 RequestMappingHandlerAdapter 时,已经被设置到缓存中)

  1. 处理自身的 InitBinder 方法(第一次调用后,保存到缓存中)

添加顺序是先全局后自身


ModelFactory 的初始化


ModelFactory:用来处理 Model,它有两个作用:

  • 处理器处理前对 Model 进行初始化;

  • 处理完请求后对 Model 参数进行更新;


Model 的初始化包含三个部分:

  1. 将原来的 SessionAttributes 中的值设置到 Model 中;

  2. 执行注视了 @ModelAttribute 的方法并将其值设置到 model;

  3. 处理器中注释了 @ModelAttribute 的参数如果同时也在 SessionAttributes 中配置了;

而且,在 mavContainer 中没有值,则从全部 SessionAttributes 中查找出并设置进去

(这里可能是其他处理器设置的值)


Model 的更新策略:

  • 先对 SessionAttributes 进行设置;

  • 如果处理器调用了 SessionStatus#setComplete,则清空 SessionAttributes;

  • 否则,将 mavContainer 中的 defaultModel 中响应参数设置到 SessionAttributes;

  • 然后,按需要给 Model 设置参数对应的 BindingResult;


getModelFactory 源码:

private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {  // 获取SessionAttributesHandler  SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);  // 获取有@ModelAttribute没有@RequestMapping的方法,使用后添加到缓存  Class<?> handlerType = handlerMethod.getBeanType();  Set<Method> methods = this.modelAttributeCache.get(handlerType);  if (methods == null) {    methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);    this.modelAttributeCache.put(handlerType, methods);  }  List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();  // 先添加全局@ControllerAdvice方法,后添加当前处理器定义的@ModelAttribute方法  for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) {    if (entry.getKey().isApplicableToBeanType(handlerType)) {      Object bean = entry.getKey().resolveBean();      for (Method method : entry.getValue()) {        attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));      }    }  }  for (Method method : methods) {    Object bean = handlerMethod.getBean();    attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));  }
// 创建ModelFactory return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);}
复制代码

new ModelFactory(attrMethods, binderFactory, sessionAttrHandler)使用了 3 个参数:

  1. 注释了 @ModelAttribute 的方法

@ControllerAdvice 类中定义的全局 @ModelAttribute 方法

处理器自己的 @ModelAttribute 方法

先添加全局,后添加自己的

  1. WebDataBinderFactory,前面创建出来的

  2. SessionAttributesHandler,由 getSessionAttributesHandler 获得


ServletInvocableHandlerMethod 初始化

ServletInvocableHandlerMethod 继承自 HandlerMethod,并且可以直接被执行;

此方法就是情急请求的处理方法:参数绑定、请求处理、返回值处理都是在此方法中完成的;


部分源码:

ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);invocableMethod.setDataBinderFactory(binderFactory);invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) { return new ServletInvocableHandlerMethod(handlerMethod);}
复制代码

首先,使用 handlerMethod 新建了一个 ServletInvocableHandlerMethod 类;

然后,设置 argumentResolvers、returnValueHandlers、binderFactory、parameterNameDiscoverer;


ModelAndViewContainer 初始化:


ModelAndViewContainer 用于保存 Model 和 View


部分源码:

// 创建ModelAndViewContainer用于保存Model和ViewModelAndViewContainer mavContainer = new ModelAndViewContainer();// 将FlashMap中的数据设置到ModelmavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));// 使用modelFactory将SessionAttributes和注释了@ModelAttributede的方法的参数设置到ModelmodelFactory.initModel(webRequest, mavContainer, invocableMethod);// 根据配置对ignoreDefaultModelOnRedirect进行设置mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
复制代码

ModelAndViewContainer 贯穿于整个处理过程,对 mavContainer 设置了 3 部分内容:

  1. 将 FlashMap 中的数据设置到 Model ;

  2. 使用 modelFactory 将 SessionAttributes 和注释了 @ModelAttributede 的方法的参数设置到 Model;

  3. 根据配置对 ignoreDefaultModelOnRedirect 进行设置;


public void initModel(NativeWebRequest request, ModelAndViewContainer container,    HandlerMethod handlerMethod) throws Exception {
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request); container.mergeAttributes(sessionAttributes); invokeModelAttributeMethods(request, container);
for (String name : findSessionAttributeArguments(handlerMethod)) { if (!container.containsAttribute(name)) { Object value = this.sessionAttributesHandler.retrieveAttribute(request, name); if (value == null) { throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name); } container.addAttribute(name, value); } }}
复制代码


invokeAndHandler 执行请求


invocableMethod.invokeAndHandle(webRequest, mavContainer);
复制代码


直接调用了 ServletInvocableHandlerMethod#invokeAndHandle 方法执行请求,后续这部分会详细分析;


getModelAndView 请求处理完成后的后置处理


相关源码:

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,    ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
// 调用updateModel更新Model(设置了SessionAttributes和给Model设置BindingResult) modelFactory.updateModel(webRequest, mavContainer); if (mavContainer.isRequestHandled()) { return null; } // 根据mavContainer创建ModelAndView ModelMap model = mavContainer.getModel(); ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } // 如果model是RedirectAttributes类型,将值设置到FlashMap if (model instanceof RedirectAttributes) { Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } return mav;}
复制代码


一共做了 3 件事:

调用 updateModel 更新 Model(设置了 SessionAttributes 和给 Model 设置 BindingResult)根据 mavContainer 创建 ModelAndView如果model 是 RedirectAttributes 类型,将值设置到 FlashMap
这里的 model 只有在处理器返回 redirect 类型的视图是才能是 RedirectAttributes 类型在不返回 redirect 类型视图的处理器中,即使使用 RedirectAttributes 设置了变量也不会保存到 FlashMap
这部分会在ModelAndViewContainer时详细分析...
复制代码



结尾


至此,整个 RequestMappingHandlerAdapter 处理请求的过程就分析完了;

还有很多内部组件并没有分析到,接下来会一一进行分析;


用户头像

Brave

关注

还未添加个人签名 2018.12.13 加入

还未添加个人简介

评论

发布
暂无评论
SpringMVC源码分析-HandlerAdapter(3)-RequestMappingHandlerAdapter请求处理