RequestMappingHanderAdapter 的请求处理
RequestMappingHanderAdapter 处理请求的入口方法是 handleInternal:
@Override
protected 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或attributeTypes
public 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和View
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// 将FlashMap中的数据设置到Model
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// 使用modelFactory将SessionAttributes和注释了@ModelAttributede的方法的参数设置到Model
modelFactory.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 处理请求的过程就分析完了;
还有很多内部组件并没有分析到,接下来会一一进行分析;
评论