本篇应用的 Spring 版本为 5.2.2.RELEASE
九大组件
SpringMVC 简直所有的性能都由九大组件来实现,所以明确九大组件的作用,对于学习 SpringMVC 来说十分重要。
/** 文件上传解析器 */
private MultipartResolver multipartResolver;
/** 区域解析器,用于国际化 */
private LocaleResolver localeResolver;
/** 主题解析器 */
private ThemeResolver themeResolver;
/** Handler 映射信息 */
private List<HandlerMapping> handlerMappings;
/** Handler 适配器 */
private List<HandlerAdapter> handlerAdapters;
/** Handler 执行异样解析器 */
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/** 申请到视图的转换器 */
private RequestToViewNameTranslator viewNameTranslator;
/** SpringMVC 容许重定向时携带参数,存在 session 中,用完就销毁,所以叫 FlashMap */
private FlashMapManager flashMapManager;
/** 视图解析器 */
private List<ViewResolver> viewResolvers;
复制代码
- HandlerMapping:Handler 映射信息,依据申请携带的 url 信息查找处理器(Handler)。每个申请都须要对应的 Handler 解决。
- HandlerAdapter:Handler 适配器,SpringMVC 没有间接调用处理器(Handler),而是通过 HandlerAdapter 来调用,次要是为了对立 Handler 的调用形式
- ViewResolver:视图解析器,用来将字符串类型的视图名称解析为 View 类型的视图。ViewResolver 须要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染,具体的渲染过程则交由不同的视图本人实现。
- MultipartResolver:文件上传解析器,次要用来解决文件上传申请
- HandlerExceptionResolver:Handler 执行异样解析器,用来对异样进行对立解决
- RequestToViewNameTranslator:申请到视图的转换器
- LocaleResolver:区域解析器,用于反对国际化
- FlashMapManager:SpringMVC 容许重定向时携带参数,存在 session 中,用完就销毁,所以叫 FlashMap
- ThemeResolver:主题解析器,用于反对不同的主题
九大组件中最重的的前三个,HandlerMapping、HandlerAdapter 和 ViewResolver,因为这是浏览源码时,避不开的三个组件。
《2020 最新 Java 根底精讲视频教程和学习路线!》
调试筹备
搭建一个根本的 Spring web 我的项目即可
Controller 局部
@Controller
public class IndexController {@RequestMapping("/index/home")
public String home(String id, Student student, @RequestParam("code") String code) {System.out.println(student.getName());
return "index";
}
@ResponseBody
@RequestMapping("/index/list")
public String list() {return "success";}
}
复制代码
Entity 局部
public class Student {
private String name;
private Integer gender;
// getter、setter
}
复制代码
还是那句话,Spring 源码十分宏大,不能只见树木不见森林,须要有针对性的浏览,所以本篇只须要关注主体流程即可。
外围办法
咱们都晓得,SpringMVC 有一个用来散发申请的前端控制器 DispatcherServlet,其中用来解决申请的办法就是 doService,该办法定义如下
doService
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
if (this.flashMapManager != null) {FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
// 真正执行的办法
doDispatch(request, response);
}
finally {if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
复制代码
doDispatch
doDispatch 是 doService 中真正用来解决申请的办法
/**
* 理论解决申请的办法
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 校验是否是文件上传申请
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// 为以后申请找到一个适合的处理器(Handler)// 返回值是一个 HandlerExecutionChain,也就是处理器执行链
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 依据 HandlerExecutionChain 携带的 Handler 找到适合的 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// 解决 GET 申请的缓存
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}
}
// 执行拦截器的 preHandle 办法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}
// Actually invoke the handler.
// 利用 HandlerAdapter 来执行 Handler 里对应的解决办法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {return;}
// 如果没有设置视图,则利用默认的视图名
applyDefaultViewName(processedRequest, mv);
// 执行拦截器的 postHandle 办法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {dispatchException = ex;}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 依据 ModelAndView 对象解析视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {cleanupMultipart(processedRequest);
}
}
}
}
复制代码
该办法就是 SpringMVC 解决申请的整体流程,其中波及到几个重要的办法。
getHandler
该办法定义如下
/**
* Return the HandlerExecutionChain for this request.
* 为这个 request 返回一个 HandlerExecutionChain
*/
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {return handler;}
}
}
return null;
}
复制代码
调试信息如下
依据调试信息能够看出,getHandler 办法次要是从 List handlerMappings 汇合中遍历查找一个适合的处理器(Handler),返回的后果是一个 HandlerExecutionChain。而后再依据 HandlerExecutionChain 里携带的 Handler 去获取 HandlerAdapter。
getHandlerAdapter
getHandlerAdapter 办法定义如下
/**
* Return the HandlerAdapter for this handler object.
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {for (HandlerAdapter adapter : this.handlerAdapters) {if (adapter.supports(handler)) {return adapter;}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
复制代码
调试信息如下
同样 getHandlerAdapter 办法次要是从 List handlerAdapters 汇合中遍历查找一个适合的处理器适配器(HandlerAdapter),返回的后果是一个 HandlerAdapter。
能够看到此处 HandlerAdapter 真正的实现类是 RequestMappingHandlerAdapter。
processDispatchResult
processDispatchResult 办法次要依据办法执行实现后封装的 ModelAndView,转发到对应页面,定义如下
/**
* Handle the result of handler selection and handler invocation, which is
* either a ModelAndView or an Exception to be resolved to a ModelAndView.
*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {if (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();}
else {Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
// 次要调用该办法渲染视图
render(mv, request, response);
if (errorView) {WebUtils.clearErrorRequestAttributes(request);
}
}
else {if (logger.isTraceEnabled()) {logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
复制代码
render
render 办法定义如下
/**
* Render the given ModelAndView.
* <p>This is the last stage in handling a request. It may involve resolving the view by name.
* @param mv the ModelAndView to render
* @param request current HTTP servlet request
* @param response current HTTP servlet response
* @throws ServletException if view is missing or cannot be resolved
* @throws Exception if there's a problem rendering the view
*/
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
// 依据给定的视图名称,解析获取 View 对象
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {throw new ServletException("Could not resolve view with name'" + mv.getViewName() +
"'in servlet with name'" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a" +
"View object in servlet with name'" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isTraceEnabled()) {logger.trace("Rendering view [" + view + "]");
}
try {if (mv.getStatus() != null) {response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {if (logger.isDebugEnabled()) {logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
复制代码
resolveViewName
resolveViewName 办法定义如下
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {if (this.viewResolvers != null) {for (ViewResolver viewResolver : this.viewResolvers) {View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {return view;}
}
}
return null;
}
复制代码
调试信息如下
依据调试信息能够看到真正解析视图的 ViewResolver 的是 InternalResourceViewResolver 类,也就是咱们常常配置的一项类型
<!-- 定义视图文件解析 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".html" />
</bean>
复制代码
至此咱们就失去了 SpringMVC 解决申请的残缺逻辑
SpringMVC 解决申请的整个流程曾经梳理分明了。
然而,有两个重要的问题没有解决,那就是:参数绑定和返回值解决。
因为在编写 Controller 外面的办法的时候,各种类型的参数都有,SpringMVC 是怎么解决不同类型的参数的呢?> SpringMVC 解决申请实现后,肯定会返回 ModelAndView 吗,如果加了 @ResponseBody 注解呢?
参数绑定
在整个流程中,还有一个最重要的办法,那就是真正执行 handler 的办法,参数的绑定和返回值的解决都在这个办法里,也就是
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
复制代码
handle
handle 办法的作用是依据申请参数,执行真正的解决办法,并且返回适合的 ModelAndView 对象,也有可能返回 null。该办法定义如下 在 AbstractHandlerMethodAdapter 类中
/**
* This implementation expects the handler to be an {@link HandlerMethod}.
*/
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {return handleInternal(request, response, (HandlerMethod) handler);
}
复制代码
能够看到这个办法实现只有一行代码
handleInternal
持续深刻 handleInternal 办法
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// 校验指定的申请以获取受反对的办法类型(GET、POST 等)和所需的 session
checkRequest(request);
// 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) {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...
// 真正执行 handler 的办法
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {prepareResponse(response);
}
}
return mav;
}
复制代码
invokeHandlerMethod
持续深刻 invokeHandlerMethod 办法
/**
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
* if view resolution is required.
* 执行 @RequestMapping 标注的 handler 办法,如果须要解析视图就筹备一个 ModelAndView
*/
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// HandlerMethod 接口封装执行办法的信息,提供对办法参数,办法返回值,办法正文等的便捷拜访。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 能够看做 ModelAndView 的上下文容器,关联着 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();
LogFormatUtils.traceDebug(logger, traceOn -> {String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 真正执行 Handler 的办法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {return null;}
// 获取 ModelAndeView 对象
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {webRequest.requestCompleted();
}
}
复制代码
invokeAndHandle
invokeAndHandle 办法的作用是执行并解决真正响应申请的办法,该办法定义如下
/**
* Invoke the method and handle the return value through one of the
* configured {@link HandlerMethodReturnValueHandler 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 {
// 执行 handler 的办法
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 {
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(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 HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
* The {@code providedArgs} 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.
* <p>Delegates to {@link #getMethodArgumentValues} and calls {@link #doInvoke} with the
* resolved arguments.
* @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
* @throws Exception raised if no suitable argument resolver can be found,
* or if the method raised an exception
* @see #getMethodArgumentValues
* @see #doInvoke
*/
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {logger.trace("Arguments:" + Arrays.toString(args));
}
// 执行
return doInvoke(args);
}
复制代码
真正的执行无非就是通过反射 invoke,所以更重要的是参数是如何绑定的,详情就在 getMethodArgumentValues 办法
getMethodArgumentValues
getMethodArgumentValues 办法用于从 request 申请中获取真正的参数,返回的是 Object 数组,该办法定义如下
/**
* Get the method argument values for the current request, checking the provided
* argument values and falling back to the configured argument resolvers.
* <p>The resulting array will be passed into {@link #doInvoke}.
* @since 5.1.2
*/
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);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {continue;}
if (!this.resolvers.supportsParameter(parameter)) {throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
复制代码
依据调试信息能够看到,用来解决申请参数的类是 HandlerMethodArgumentResolver 接口的实现类 HandlerMethodArgumentResolverComposite,此时正在解决的参数是一个 Student 对象,并且曾经把值注绑定了,也就是说真正执行绑定的是办法 resolveArgument
resolveArgument
resolveArgument 是真正执行绑定的的办法
/**
* Iterate over registered
* {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}
* and invoke the one that supports it.
* @throws IllegalArgumentException if no suitable argument resolver is found
*/
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 获取适合的参数解析器
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
// 执行参数绑定
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
复制代码
getArgumentResolver
getArgumentResolver 该办法用于执行参数的绑定,定义如下
/**
* Find a registered {@link HandlerMethodArgumentResolver} that supports
* the given method parameter.
*/
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
复制代码
该办法的逻辑就是先从 argumentResolver 缓存中找到可能执行参数绑定的 HandlerMethodArgumentResolver,如果找不到就从 HandlerMethodArgumentResolver 找,SpringMVC 反对的 HandlerMethodArgumentResolver 一共有 26 种,用来解析各种类型的参数
依据博主的调试能够晓得
- RequestParamMethodArgumentResolver:解决一般参数(根本类型、包装类型、String),不论加不加 @RequestParam 注解
- ServletModelAttributeMethodProcessor:解决 POJO 类型的参数,比方自定义的 Student 对象
- RequestResponseBodyMethodProcessor:解决 @RequestBody 注解类型的参数
resolveArgument
因为不同类型的参数有不同的 HandlerMethodArgumentResolver 来解决,此处选取 POJO 类型参数的注入实现,对应的参数解析类是 ModelAttributeMethodProcessor,其中 resolveArgument 办法用来解析(绑定)参数办法定义如下
/**
* Resolve the argument from the model or if not found instantiate it with
* its default if it is available. The model attribute is then populated
* with request values via data binding and optionally validated
* if {@code @java.validation.Valid} is present on the argument.
* @throws BindException if data binding and validation result in an error
* and the next method parameter is not of type {@link Errors}
* @throws Exception if WebDataBinder initialization fails
*/
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
// 获取参数名
String name = ModelFactory.getNameForParameter(parameter);
// 获取参数上的 ModelAttribute 注解
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
if (ann != null) {mavContainer.setBinding(name, ann.binding());
}
Object attribute = null;
BindingResult bindingResult = null;
if (mavContainer.containsAttribute(name)) {attribute = mavContainer.getModel().get(name);
}
else {
// Create attribute instance
try {
// 创立参数类型的实例(未注入值),底层就是通过反射调用构造方法
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
catch (BindException ex) {if (isBindExceptionRequired(parameter)) {
// No BindingResult parameter -> fail with BindException
throw ex;
}
// Otherwise, expose null/empty value and associated BindingResult
if (parameter.getParameterType() == Optional.class) {attribute = Optional.empty();
}
bindingResult = ex.getBindingResult();}
}
if (bindingResult == null) {
// Bean property binding and validation;
// skipped in case of binding failure on construction.
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {if (!mavContainer.isBindingDisabled(name)) {
// 真正执行绑定(值注入)的办法
bindRequestParameters(binder, webRequest);
}
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {throw new BindException(binder.getBindingResult());
}
}
// Value type adaptation, also covering java.util.Optional
if (!parameter.getParameterType().isInstance(attribute)) {attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
bindingResult = binder.getBindingResult();}
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
}
复制代码
依据调试信息也能够看到 bindRequestParameters(binder, webRequest) 执行实现之后,POJO 类型的参数曾经实现了绑定。
bindRequestParameters
/**
* This implementation downcasts {@link WebDataBinder} to
* {@link ServletRequestDataBinder} before binding.
* @see ServletRequestDataBinderFactory
*/
@Override
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
Assert.state(servletRequest != null, "No ServletRequest");
ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
// 执行绑定的办法
servletBinder.bind(servletRequest);
}
复制代码
bind
持续深刻 bind 办法
public void bind(ServletRequest request) {
// 获取所有参数的键值对
MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
// 解决文件上传申请
MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
if (multipartRequest != null) {bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
}
// 把 url 中携带的参数也退出到 MutablePropertyValues
addBindValues(mpvs, request);
// 执行绑定(注入值)doBind(mpvs);
}
复制代码
因为调用档次过深,所以无奈一步步列出上面的步骤,doBind 办法的原理还是通过调用 POJO 对象里的 setter 办法设置值,能够查看最终的调试信息
依据调试信息能够看到,最终执行的还是 POJO 对象的 setter 办法,具体执行的类是 BeanWrapperImpl。
理解了参数的绑定,再来看返回值的解决。
返回值解决
invokeAndHandle
回到源码 invokeAndHandle 办法处(ServletInvocableHandlerMethod 类中),该办法定义如下
/**
* Invoke the method and handle the return value through one of the
* configured {@link HandlerMethodReturnValueHandler 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) || 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 {
// 真正解决不同类型返回值的办法
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
复制代码
真正解决不同类型的返回值的办法是 handleReturnValue 办法
handleReturnValue
/**
* Iterate over registered {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers} and invoke the one that supports it.
* @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
*/
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 依据返回值个返回值类型选取适合的 HandlerMethodReturnValueHandler
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {throw new IllegalArgumentException("Unknown return value type:" + returnType.getParameterType().getName());
}
// 真正的解决返回值
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
复制代码
selectHandler
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {continue;}
if (handler.supportsReturnType(returnType)) {return handler;}
}
return null;
}
复制代码
依据调试信息能够看到,SpringMVC 为返回值提供了 15 个 HandlerMethodReturnValueHandler 的实现了来解决不同类型的返回值。
事实上,用来解决 @ResponseBody 类型的是 RequestResponseBodyMethodProcessor。
如果对前文参数绑定还有印象的话,会发现 @RequestBody 类型参数绑定也是用的这个类。
持续跟进 RequestResponseBodyMethodProcessor 类的 handleReturnValue 办法
handleReturnValue
RequestResponseBodyMethodProcessor 类的 handleReturnValue 办法定义如下
这里设置了一个十分重要的属性 requestHandled,这个属性关系到是否须要返回 ModelAndView 对象
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// 设置该申请是否已在处理程序中齐全解决,例如 @ResponseBody 办法不须要视图解析器,此处就能够设置为 true。// 当控制器办法申明类型为 ServletResponse 或 OutputStream 的参数时,也能够设置此标记为 true。// 这个属性设置成 true 之后,下层 getModelAndView 获取 ModelAndView 时会返回 Null,因为不须要视图。// 默认值为 false
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
// 底层就是利用 java.io.OutputStreamWriter 类把返回值写到网络 IO
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
复制代码
持续深刻 writeWithMessageConverters 办法,一步步调试到最初,底层就是利用 java.io.OutputStreamWriter 类把返回值写到网络 IO
因为 handleReturnValue 把 requestHandled 设置成了 true,下层在调用 getModelAndView 办法时会返回 null,示意该申请不须要视图。感兴趣的同学本人调试一下便知。
总结
本文次要从源码的浏览和调试的角度,整体的解说了 SpringMVC 解决申请的整个流程,并且解说了参数的绑定以及返回值的解决。置信大家看完后,联合本人的调试信息,会对 SpringMVC 的申请处理过程有一个更深刻的了解。
链接:https://juejin.cn/post/690348…