请求进来,先找到能处理的controller,再找到处理器适配器,完成controller方法的调用。
调用方法的第一步就是先要获取方法参数,这里也是请求参数。然后处理返回结果,再然后渲染页面。
1.handle方法
调用刚刚找到的RequestMappingHandlerAdapter的handle方法,完成请求的执行。
2.handleInternal方法
然后调用RequestMappingHandlerAdapter类的handleInternal方法,
这里有三个步骤
1.对请求进行检查,
2.然后调用invokeHandlerMethod方法返回ModelAndView类型的对象,
3.然后封装response。
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
//第一步
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...
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.invokeHandlerMethod方法
这个方法主要是获取argumentResolvers 参数解析器,returnValueHandlers 返回值处理器,配置ModelAndViewContainer视图容器,然后反射调用controller方法。
@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);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
//设置参数解析器,大概有26个,在requestMappingHandlerAdapter初始化时候加载的。
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
//设置返回值处理器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
//视图模型容器
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);
}
//反射调用controller方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
4.invokeAndHandle方法
主要看这个方法,这个方法执行完, 返回值就有了,解析方法参数(请求参数)肯定也在这里。
5.invokeForRequest方法
进来发现这里有个getMethodArgumentValues,这一看就是处理参数的。
处理完参数调用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);
}
6.getMethodArgumentValues方法
获取到方法的参数,然后遍历,去请求中找对应的参数,这个功能是参数解析器完成的。找参数解析器是根据注解。解析参数是根据resolveArgument方法
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 {
//调用参数解析器的resolveArgument方法,解析参数
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;
}
获取到的两个参数
对应controller中的参数
7.resolveArgument方法
获取到参数解析器,我的第一个参数是HttpServlerRequest,所以调用HttpServlerRequestMethodArgumentResovler来处理这个参数。
8.继续调用resolveArgument方法
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
//参数名字
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
Object resolvedName = resolveStringValue(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
//根据名字解析值
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
if (arg == null) {
if (namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
9.resolveName方法
目前已经获得了方法的参数,现在需要从请求中获取这个参数对应的值,然后赋值给这个参数。就是用的下面这个方法。
@Nullable
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
return (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
}
就是定义了一个map,然后调用request.getAttribute方法,可以看出URI_TEMPLATE_VARIABLES_ATTRIBUTE的值,而且是在HandlerMapping中定义的。看来是在相关方法中将这个值setAttribute的,存的应该也是一个map。第二个常量SCOPE_REQUEST代表request,还有个与之对应的是SCOPE_SESSION。
从头仿真看看是什么时候将这个请求携带的参数值setAttribute的。
最终发现是在handleMatch方法中,怎么最终发现的? 一个断点一个断点的试,一直看着request中的attribute的值,慢慢就找到了~~
然后回头看了下请求处理过程第一篇,发现handleMatch方法是在返回最佳controller之前的一个方法。
至此搞懂了@PathVariable这种路径传参的方式(上图中请求没这个注解,为了测试resolveName方法加的)。可是参数解析器还有20多个吧。哎 ,加油~~