java小库_Java攻城师

本篇使用的Spring版本为5.2.2.RELEASE

九大组件

SpringMVC几乎所有的功能都由九大组件来完成,所以明白九大组件的作用,对于学习SpringMVC来说非常重要。/** 文件上传解析器 */

private MultipartResolver multipartResolver;

/** 区域解析器,用于国际化 */

private LocaleResolver localeResolver;

/** 主题解析器 */

private ThemeResolver themeResolver;

/** Handler映射信息 */

private List handlerMappings;

/** Handler适配器*/

private List handlerAdapters;

/** Handler执行异常解析器 */

private List handlerExceptionResolvers;

/** 请求到视图的转换器 */

private RequestToViewNameTranslator viewNameTranslator;

/** SpringMVC允许重定向时携带参数,存在session中,用完就销毁,所以叫FlashMap */

private FlashMapManager flashMapManager;

/** 视图解析器 */

private List 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 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;

}

复制代码

调试信息如下

activities?page=5

根据调试信息可以看出,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");

}

复制代码

调试信息如下

activities?page=5

同样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.

*

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 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;

}

复制代码

调试信息如下

activities?page=5

根据调试信息可以看到真正解析视图的ViewResolver的是InternalResourceViewResolver类,也就是我们经常配置的一项类型

复制代码

至此我们就得到了SpringMVC处理请求的完整逻辑

activities?page=5

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.

*

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.

*

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.

*

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;

}

复制代码

activities?page=5

根据调试信息可以看到,用来处理请求参数的类是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种,用来解析各种类型的参数

activities?page=5

根据博主的调试可以知道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 bindingResultModel = bindingResult.getModel();

mavContainer.removeAttributes(bindingResultModel);

mavContainer.addAllAttributes(bindingResultModel);

return attribute;

}

复制代码

activities?page=5

根据调试信息也可以看到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方法设置值,可以查看最终的调试信息

activities?page=5

根据调试信息可以看到,最终执行的还是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;

}

复制代码

activities?page=5

根据调试信息可以看到,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

activities?page=5

由于handleReturnValue把requestHandled设置成了true,上层在调用getModelAndView方法时会返回null,表示该请求不需要视图。感兴趣的同学自己调试一下便知。

总结

本文主要从源码的阅读和调试的角度,整体的讲解了SpringMVC处理请求的整个流程,并且讲解了参数的绑定以及返回值的处理。相信大家看完后,结合自己的调试信息,会对SpringMVC的请求处理过程有一个更深入的理解。查看原文

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值