HandlerAdapter之RequestMappingHandlerAdapter处理请求
一、类图
1.1 HandlerAdapter类图
1.2 RequestMappingHandlerAdapter类图
二、处理请求流程
(1) 检查给定请求的请求方法是否支持,是否必须有会话
(2) 创建WebDataBinderFactory:Handler整个继承体系上有@InitBinder注解的方法,以及@ControllerAdvice注解标注的类中有@InitBinder注解的方法
(3) 创建ModelFactory:获取Handler上的@SessionAttributes注解,获取Handler中没有标注@RequestMapping但标注了@ModelAttribute注解的方法,@ControllerAdvice注解标注的类中没有标注@RequestMapping但标注了@ModelAttribute注解的方法
(4) 创建ServletInvocableHandlerMethod:封装了HandlerMethod,设置HandlerMethodArgumentResolverComposite、HandlerMethodReturnValueHandlerComposite、WebDataBinderFactory、ParameterNameDiscoverer
(5) 创建ModelAndViewContainer:设置请求中的FlashMap(Input)
(6) 调用ModelFactory#initModel:按以下顺序往ModelAndViewContainer中填充model:
在session中检索{@code @SessionAttributes}注解指定的会话属性存在的值。
调用{@code @ModelAttribute}标注的方法,返回值保存到ModelAndViewContainer中。
查找{@code @ModelAttribute}标注的方法参数,先从ModelAndViewContainer获取,不存在的从session中获取,如果session中也没有抛出异常。
(7) 创建WebAsyncManager:异步处理使用
(8) 调用ServletInvocableHandlerMethod#invokeAndHandle方法处理请求:
找到匹配方法参数的HandlerMethodArgumentResolver,解析出参数
反射调用方法
根据方法返回值和返回值类型,找到匹配的HandlerMethodArgumentResolver,处理返回值
(9) 获取ModelAndView:根据ModelAndViewContainer、ModelFactory、ServletWebRequest获取ModelAndView
调用ModelFactory#updateModel方法:将{@code @SessionAttributes}指定的属性提升到会话。如有必要,添加{@link BindingResult}属性。
三、处理请求
3.1 AbstractHandlerMethodAdapter
3.1.1 是否支持Handler supports
AbstractHandlerMethodAdapter
public final boolean supports(Object handler) {
// handler必须实现HandlerMethod接口,supportsInternal 留给子类实现,传送门:3.2.1
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
3.1.2 调用Handler处理请求 handle
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// handleInternal 留给子类实现,传送门:3.2.2
return handleInternal(request, response, (HandlerMethod) handler);
}
3.2 RequestMappingHandlerAdapter
3.2.1 是否支持处理当前Handler supportsInternal
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
3.2.2 处理请求 handleInternal
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// 检查给定的请求以获取受支持的方法和所需的会话(如果有)
checkRequest(request);
// 如果需要,在同步块中执行invokeHandlerMethod
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 没有可用的HttpSession->不需要同步块执行
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 不需要会话同步
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
3.2.3 checkRequest
// 检查给定的请求以获取受支持的方法和所需的会话(如果有)
protected final void checkRequest(HttpServletRequest request) throws ServletException {
// 检查是否支持请求方法
String method = request.getMethod();
if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
}
// 检查是否需要会话
if (this.requireSession && request.getSession(false) == null) {
throw new HttpSessionRequiredException("Pre-existing session required but none found");
}
}
3.2.4 invokeHandlerMethod
@ModelAttribute一般解决同一个Controller中共性问题(如果放在一个公共的Contrller父类,也可以解决多Controller间共性问题)。@ControllerAdvice主要解决多Contrller中的共性问题,如公共数据、全局异常处理、全局入参出参转换等。可包含@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法,这些方法默认将对所有Contrller的@RequestMapping方法启用(可通过@ControllerAdvice属性值限制范围)
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 数据绑定。获取整个继承体系上有@InitBinder注解的方法,以及@ControllerAdvice注解标注的类中有@InitBinder注解的方法。@InitBinder标注的方法可以设置数据绑定属性,比如:属性编辑器
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 往Model/Session中保存数据,从Model中获取数据
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 数据绑定器
invocableMethod.setDataBinderFactory(binderFactory);
// 参数名称发现器
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// 从FlashMap中获取数据,之前请求保存的数据
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// 1、处理@SessionAttributes注解
// 2、调用@ModelAttribute注解标注的方法,并将返回值保存到mavContainer中。实际上保存在ModelAndViewContainer的ModelMap中
// 3、解析方法参数上的@ModelAttribute注解,从@SessionAttributes标注的session中获取,保存到ModelMap中。
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
// 默认情况下,在渲染和重定向方案中都使用“默认”模型的内容。另外,控制器方法可以声明{@code RedirectAttributes}类型的参数,并使用它提供属性以准备重定向URL。
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();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
// 创建一个嵌套的ServletInvocableHandlerMethod子类,该子类返回给定的值(如果该值为1,则引发Exception),而不是实际调用控制器方法。这在处理异步返回值(例如Callable,DeferredResult,ListenableFuture)时很有用。
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 调用Handler方法处理请求,传送门3.3.1
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
3.2.4.1 getDataBinderFactory
// 获取整个继承体系上有@InitBinder注解的方法,以及@ControllerAdvice注解标注的类中有@InitBinder注解的方法
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
Class<?> handlerType = handlerMethod.getBeanType();
// 初次获取为NULL
Set<Method> methods = this.initBinderCache.get(handlerType);
if (methods == null) {
// 整个继承体系上获取有@InitBinder注解的方法
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
// @ControllerAdvice注解标注的类中有@InitBinder注解的方法
this.initBinderAdviceCache.forEach((clazz, methodSet) -> {
// 是否能适用当前Handler
if (clazz.isApplicableToBeanType(handlerType)) {
Object bean = clazz.resolveBean();
for (Method method : methodSet) {
// 用InvocableHandlerMethod封装bean和method
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
});
// 整个继承体系上有@InitBinder注解的方法,也加入到initBinderMethods
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
return createDataBinderFactory(initBinderMethods);
}
3.2.4.2 getModelFactory
// 获取类上的@SessionAttributes注解,获取没有标注@RequestMapping但标注了@ModelAttribute注解的方法,标注了@ControllerAdvice注解的类中没有标注@RequestMapping但标注了@ModelAttribute注解的方法
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
// 获取类上的@SessionAttributes注解
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
Class<?> handlerType = handlerMethod.getBeanType();
Set<Method> methods = this.modelAttributeCache.get(handlerType);
if (methods == null) {
// 获取没有标注@RequestMapping但标注了@ModelAttribute注解的方法,数据预处理
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
// this.modelAttributeAdviceCache是标注了@ControllerAdvice注解的类中没有标注@RequestMapping但标注了@ModelAttribute注解的方法
this.modelAttributeAdviceCache.forEach((clazz, methodSet) -> {
if (clazz.isApplicableToBeanType(handlerType)) {
Object bean = clazz.resolveBean();
for (Method method : methodSet) {
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});
for (Method method : methods) {
Object bean = handlerMethod.getBean();
// InvocableHandlerMethod封装了binderFactory, Handler, method
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
3.2.4.3 获取类上的@SessionAttributes注解 getSessionAttributesHandler
// 获取类上的@SessionAttributes注解
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) {
// 获取Handler上的@SessionAttributes注解
sessionAttrHandler = new SessionAttributesHandler(handlerType, this.sessionAttributeStore);
this.sessionAttributesHandlerCache.put(handlerType, sessionAttrHandler);
}
}
}
return sessionAttrHandler;
}
3.3.4.5 getModelAndView
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
// 将列为{@code @SessionAttributes}的模型属性提升到会话。 如有必要,添加{@link BindingResult}属性。
modelFactory.updateModel(webRequest, mavContainer);
// 请求是否已在处理程序中完全处理,响应已经写回客户端
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
// 请求重定向,请求参数保存到FlashMap中
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
3.2.9 设置缓存时间 applyCacheSeconds
// 应用给定的缓存秒数并生成相应的HTTP标头,即在为正数的情况下允许缓存给定的秒数,如果给定值为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);
}
}
3.2.10 设置缓存控制请求头 applyCacheControl
// 根据给定的设置设置HTTP Cache-Control
protected final void applyCacheControl(HttpServletResponse response, CacheControl cacheControl) {
String ccValue = cacheControl.getHeaderValue();
if (ccValue != null) {
// Set computed HTTP 1.1 Cache-Control header
response.setHeader(HEADER_CACHE_CONTROL, ccValue);
if (response.containsHeader(HEADER_PRAGMA)) {
// Reset HTTP 1.0 Pragma header if present
response.setHeader(HEADER_PRAGMA, "");
}
if (response.containsHeader(HEADER_EXPIRES)) {
// Reset HTTP 1.0 Expires header if present
response.setHeader(HEADER_EXPIRES, "");
}
}
}
3.3 ServletInvocableHandlerMethod
3.3.1 调用Handler方法处理请求 invokeAndHandle
// 3.2.4调用时为传入providedArgs参数值,providedArgs为NULL
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) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
// 禁用内容缓存(如果需要)
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 根据返回值和返回值类型获取HandlerMethodReturnValueHandler
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
3.3.2 setResponseStatus
// 根据{@link ResponseStatus}注解设置响应状态
private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
// 获取的是HandlerMethod.responseStatus
HttpStatus status = getResponseStatus();
if (status == null) {
return;
}
HttpServletResponse response = webRequest.getResponse();
if (response != null) {
String reason = getResponseStatusReason();
if (StringUtils.hasText(reason)) {
response.sendError(status.value(), reason);
}
else {
response.setStatus(status.value());
}
}
// To be picked up by RedirectView
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
}
3.4 InvocableHandlerMethod
3.4.1 获取参数调用Handler方法 invokeForRequest
// 3.2.4调用时为传入providedArgs参数值,providedArgs为NULL
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 如果providedArgs没有匹配的参数值,获取匹配参数的HandlerMethodArgumentResolver解析参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
3.4.2 获取方法参数 getMethodArgumentValues
// 3.2.4调用时为传入providedArgs参数值,providedArgs为NULL
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 如果方法没有参数直接返回空的参数值睡数组
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
// 调用的是HandlerMethod#findProvidedArgument方法
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
// 是否有匹配的HandlerMethodArgumentResolver,比如:@RequestBody、@PathVariable等
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 根据不同HandlerMethodArgumentResolver解析出参数值,比如从请求头、请求参数、请求路径变量等
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
3.4.3 执行Handler方法 doInvoke
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(text, 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 {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}
3.5 HandlerMethod
3.5.1 获取方法参数值 findProvidedArgument
// 根据是否是参数类型的实例返回参数组。3.2.4调用时为传入providedArgs参数值,providedArgs为NULL
protected static Object findProvidedArgument(MethodParameter parameter, @Nullable Object... providedArgs) {
if (!ObjectUtils.isEmpty(providedArgs)) {
for (Object providedArg : providedArgs) {
if (parameter.getParameterType().isInstance(providedArg)) {
return providedArg;
}
}
}
return null;
}