SpringMVC源码解读--HandlerAdapter源码解读
Posted 低调的洋仔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringMVC源码解读--HandlerAdapter源码解读相关的知识,希望对你有一定的参考价值。
一路的继承关系来看的话因为实现了InitializingBean的话,然后该类首先可以调用的方法就是
afterPropertiesSet
@Override
public void afterPropertiesSet()
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();
if (this.argumentResolvers == null)
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
if (this.initBinderArgumentResolvers == null)
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
if (this.returnValueHandlers == null)
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
这个方法主要完成了这么几件事请。
1. 初始化注释了ControllerAdvice类的相关属性。
2. 初始化argumentResolvers,这个属性的话主要是用于给处理器方法和注释了@ModuleAttribute的方法设置参数。
3. 初始化initBinderArgumentResolvers,这个主要是用于给注释了@initBinder的方法设置参数。
5. 初始化returnValueHandlers,然后这个主要是用于将处理器结果处理成ModelAndView的结果类型。
private void initControllerAdviceCache()
if (getApplicationContext() == null)
return;
if (logger.isInfoEnabled())
logger.info("Looking for @ControllerAdvice: " + getApplicationContext());
// 获取注释了ControllerAdvice的bean
List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
OrderComparator.sort(beans);// 排序
List<Object> responseBodyAdviceBeans = new ArrayList<Object>();
for (ControllerAdviceBean bean : beans) // 注解了ModelAttribute且没有注解RequestMapping的方法
Set<Method> attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
if (!attrMethods.isEmpty())
this.modelAttributeAdviceCache.put(bean, attrMethods);
logger.info("Detected @ModelAttribute methods in " + bean);
//注释了initBinder的方法
Set<Method> binderMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
if (!binderMethods.isEmpty())
this.initBinderAdviceCache.put(bean, binderMethods);
logger.info("Detected @InitBinder methods in " + bean);
// 查询实现了ResponseBodyAdvice接口的类
if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType()))
responseBodyAdviceBeans.add(bean);
logger.info("Detected ResponseBodyAdvice bean in " + bean);
// 将查到的实现了ResponseBodyAdvice接口的类从前面添加到responseBodyAdvice属性。
if (!responseBodyAdviceBeans.isEmpty())
this.responseBodyAdvice.addAll(0, responseBodyAdviceBeans);
// 获取注释了ControllerAdvice的bean
List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
OrderComparator.sort(beans);// 排序
List<Object> responseBodyAdviceBeans = new ArrayList<Object>();
for (ControllerAdviceBean bean : beans) // 注解了ModelAttribute且没有注解RequestMapping的方法
Set<Method> attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
if (!attrMethods.isEmpty())
this.modelAttributeAdviceCache.put(bean, attrMethods);
logger.info("Detected @ModelAttribute methods in " + bean);
//注释了initBinder的方法
Set<Method> binderMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
if (!binderMethods.isEmpty())
this.initBinderAdviceCache.put(bean, binderMethods);
logger.info("Detected @InitBinder methods in " + bean);
// 查询实现了ResponseBodyAdvice接口的类
if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType()))
responseBodyAdviceBeans.add(bean);
logger.info("Detected ResponseBodyAdvice bean in " + bean);
// 将查到的实现了ResponseBodyAdvice接口的类从前面添加到responseBodyAdvice属性。
if (!responseBodyAdviceBeans.isEmpty())
this.responseBodyAdvice.addAll(0, responseBodyAdviceBeans);
同时注释了RequestMapping的方法,只是将返回值设置为Model而不是作为View使用了,但是不会提前执行。
实现了ResponseBodyAdvice的类,并没有在for循环中直接添加到responseBodyAdvice中,添加的代码就是this.reponseBodyAdvice.addAll(0,responseBodyAdvice);
先拍了个顺序然后进行的分发,然后将this.reposnseBodyAdvice中第零个设置成responseBodyAdviceBeans.
非常核心的属性设置过程。
/**
* Return the list of argument resolvers to use including built-in resolvers
* and custom resolvers provided via @link #setCustomArgumentResolvers.
*/
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers()
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
// Annotation-based argument resolution// 添加按照注释解析参数的解析器
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters()));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
// Type-based argument resolution// 添加按照类型解析参数的解析器
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters()));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
// Custom arguments添加自定义参数解析器,主要用于解析自定义类型
if (getCustomArgumentResolvers() != null)
resolvers.addAll(getCustomArgumentResolvers());
// Catch-all 用于解析所有类型的参数
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
自己无法编写一个PathVariable解析器来解析PathVariable中的参数,即使写出来注册到AbstractHandlerAdapater上面也不会被调用,SpringMVC的自己定义的解析器的顺序也是固定的,不可以改变的。
///
上面是初始化部分参数信息等。
实际调用的方法,handle方法调用了handleInternal方法。
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes())
// Always prevent caching in case of session attribute management.
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
else
// Uses configured default cacheSeconds setting.
checkAndPrepare(request, response, true);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession)
HttpSession session = request.getSession(false);
if (session != null)
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex)
return invokeHandleMethod(request, response, handlerMethod);
return invokeHandleMethod(request, response, handlerMethod);
checkAndPrepare方法
检查请求类型是不是支持
2.检查session是不是必须的,判断session实际是否存在、
3 给reponse设置缓存过期时间。
/**
* Check and prepare the given request and response according to the settings
* of this generator. Checks for supported methods and a required session,
* and applies the given number of cache seconds.
* @param request current HTTP request
* @param response current HTTP response
* @param cacheSeconds positive number of seconds into the future that the
* response should be cacheable for, 0 to prevent caching
* @param lastModified if the mapped handler provides Last-Modified support
* @throws ServletException if the request cannot be handled because a check failed
*/
protected final void checkAndPrepare(
HttpServletRequest request, HttpServletResponse response, int cacheSeconds, boolean lastModified)
throws ServletException
// Check whether we should support the request method.
String method = request.getMethod();
if (this.supportedMethods != null && !this.supportedMethods.contains(method))
throw new HttpRequestMethodNotSupportedException(
method, StringUtils.toStringArray(this.supportedMethods));
// Check whether a session is required.
if (this.requireSession)
if (request.getSession(false) == null)
throw new HttpSessionRequiredException("Pre-existing session required but none found");
// Do declarative cache control.
// Revalidate if the controller supports last-modified.
applyCacheSeconds(response, cacheSeconds, lastModified);
/**
* Apply the given cache seconds and generate respective HTTP headers.
* <p>That is, allow caching for the given number of seconds in the
* case of a positive value, prevent caching if given a 0 value, else
* do nothing (i.e. leave caching to the client).
* @param response the current HTTP response
* @param seconds the (positive) number of seconds into the future that
* the response should be cacheable for; 0 to prevent caching; and
* a negative value to leave caching to the client.
* @param mustRevalidate whether the client should revalidate the resource
* (typically only necessary for controllers with last-modified support)
*/
protected final void applyCacheSeconds(HttpServletResponse response, int seconds, boolean mustRevalidate)
if (seconds > 0)
cacheForSeconds(response, seconds, mustRevalidate);
else if (seconds == 0)
preventCaching(response);
// Leave caching to the client otherwise.
重点在这里
重点在这里
重点在这里
InvokeHandleMethod方法
/**
* Invoke the @link RequestMapping handler method preparing a @link ModelAndView
* if view resolution is required.
*/
private ModelAndView invokeHandleMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception
ServletWebRequest webRequest = new ServletWebRequest(request, response);
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
final 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 + "]");
requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted())
return null;
return getModelAndView(mavContainer, modelFactory, webRequest);
getDataBindFactory方法
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception
Class<?> handlerType = handlerMethod.getBeanType();
Set<Method> methods = this.initBinderCache.get(handlerType);// initBinderCache中是不是有了这个类了
if (methods == null)
methods = HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS);// 没有的话就创建好放进去
this.initBinderCache.put(handlerType, methods);
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();// 定义保存InitBinder方法的临时变量
// Global methods first所有符合条件的initBinderAdviceCache中的数据添加到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并返回。
return createDataBinderFactory(initBinderMethods);
ModleFactory是用来处理Modle的,主要包含俩功能,一个是处理器具体处理之前对Modle进行初始化,同时在处理完请求后对Model参数更新。
流程:
1. 将原来的SessionAttributes中的值设置到Model.
2. 执行相应的注释了ModelAttribute的方法并将其值设置为Model
3. 处理器中同时注释了ModelAttribute参数的如果同时在sessionAttribute中也配置了,而且在mavContainer中还没有值则从全部的SessionAttributes中查找出并设置进去。
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory)
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
Class<?> handlerType = handlerMethod.getBeanType();
Set<Method> methods = this.modelAttributeCache.get(handlerType);
if (methods == null)
methods = HandlerMethodSelector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
// Global methods first
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));
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>重点在这里>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
到底是怎么调用的?在哪里调用的?
就在这里,这个方法就是完成了参数的填充以及方法的调用的。
/**
* Invokes the method and handles the return value through one of the
* configured @link HandlerMethodReturnValueHandlers.
* @param webRequest the current request
* @param mavContainer the ModelAndViewContainer for this request
* @param providedArgs "given" arguments matched by type (not resolved)
*/
public void invokeAndHandle(ServletWebRequest webRequest,
ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null)
if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled())
mavContainer.setRequestHandled(true);
return;
else if (StringUtils.hasText(this.responseReason))
mavContainer.setRequestHandled(true);
return;
mavContainer.setRequestHandled(false);
try
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
catch (Exception ex)
if (logger.isTraceEnabled())
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
throw ex;
接下来重点又是invokeForRequest方法。
/**
* Invoke the method after resolving its argument values in the context of the given request.
* <p>Argument values are commonly resolved through @link HandlerMethodArgumentResolvers.
* The @code provideArgs parameter however may supply argument values to be used directly,
* i.e. without argument resolution. Examples of provided argument values include a
* @link WebDataBinder, a @link SessionStatus, or a thrown exception instance.
* Provided argument values are checked before argument resolvers.
* @param request the current request
* @param mavContainer the ModelAndViewContainer for this request
* @param providedArgs "given" arguments matched by type, not resolved
* @return the raw value returned by the invoked method
* @exception Exception raised if no suitable argument resolver can be found,
* or if the method raised an exception
*/
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled())
StringBuilder sb = new StringBuilder("Invoking [");
sb.append(getBeanType().getSimpleName()).append(".");
sb.append(getMethod().getName()).append("] method with arguments ");
sb.append(Arrays.asList(args));
logger.trace(sb.toString());
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled())
logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
return returnValue;
然后通过getMethodArgumentValues方法获取到参数值的数组。
/**
* Get the method argument values for the current request.
*/
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++)
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null)
continue;
if (this.argumentResolvers.supportsParameter(parameter))
try
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
catch (Exception ex)
if (logger.isDebugEnabled())
logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
throw ex;
if (args[i] == null)
String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
throw new IllegalStateException(msg);
return args;
然后后面就是doInvoke方法来进行实际的调用了。
Object returnValue = doInvoke(args);
/**
* Invoke the handler method with the given argument values.
*/
protected Object doInvoke(Object... args) throws Exception
ReflectionUtils.makeAccessible(getBridgedMethod());
try
return getBridgedMethod().invoke(getBean(), args);
catch (IllegalArgumentException ex)
assertTargetBean(getBridgedMethod(), getBean(), args);
throw new IllegalStateException(getInvocationErrorMessage(ex.getMessage(), args), ex);
catch (InvocationTargetException ex)
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException)
throw (RuntimeException) targetException;
else if (targetException instanceof Error)
throw (Error) targetException;
else if (targetException instanceof Exception)
throw (Exception) targetException;
else
String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
throw new IllegalStateException(msg, targetException);
到这里足够了method.invoke(bean, args),这个反射调用了。然后就OK了。
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
处理完请求后,在getModelAndView方法中会做几件事,
1. 调用ModelFactory的updateModel方法更新了Model包含设置了SessionAttributes和给Model设置BindingResult
2. 根据mavContainer创建了ModelAndView.
3. 如果mavContainer里面的model是RedirectAttributes类型将其设置到FlashMap中。
以上是关于SpringMVC源码解读--HandlerAdapter源码解读的主要内容,如果未能解决你的问题,请参考以下文章
SpringMVC源码解读 - HandlerMapping
SpringMVC源码解读 - RequestMapping注解实现解读 - RequestMappingInfo
SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系
SpringMVC源码解读--HandlerMapping代码解读