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 之间的类型转换;
备注:
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 方法包含两部分:
注释了 @ControllerAdvice 且符合要求的全局处理器中的 InitBinder 方法;
(创建 RequestMappingHandlerAdapter 时,已经被设置到缓存中)
处理自身的 InitBinder 方法(第一次调用后,保存到缓存中)
添加顺序是先全局后自身
ModelFactory 的初始化
ModelFactory:用来处理 Model,它有两个作用:
处理器处理前对 Model 进行初始化;
处理完请求后对 Model 参数进行更新;
Model 的初始化包含三个部分:
将原来的 SessionAttributes 中的值设置到 Model 中;
执行注视了 @ModelAttribute 的方法并将其值设置到 model;
处理器中注释了 @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 个参数:
注释了 @ModelAttribute 的方法
@ControllerAdvice 类中定义的全局 @ModelAttribute 方法
处理器自己的 @ModelAttribute 方法
先添加全局,后添加自己的
WebDataBinderFactory,前面创建出来的
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 部分内容:
将 FlashMap 中的数据设置到 Model ;
使用 modelFactory 将 SessionAttributes 和注释了 @ModelAttributede 的方法的参数设置到 Model;
根据配置对 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 处理请求的过程就分析完了;
还有很多内部组件并没有分析到,接下来会一一进行分析;
评论