要了解业务代码的返回值是如何被框架解析的,就得知道框架如何调用我们写的controller方法。实际上调用我们写的controller代码只是DispatcherServlet处理http请求并返回给前端的其中一个步骤,request处理,寻找对应的controller方法,拿到处理结果并封装,这是一个完整的流程。步骤如下:
DispatcherServlet.doDispatch ->
AbstractHandlerMethodAdapter.handle() ->
RequestMappingHandleAdapter.handleInternal() ->
RequestMappingHandleAdapter.invokeHandlerMethod() -> ServletInvocableHandlerMethod.invokeAndHandle() ->
InvocableHandlerMethod.invokeForRequest() ->
InvocableHandlerMethod.doInvoke() ->
InvocableHandlerMethod.getBridgedMethod().invoke() ->
ServletInvocableHandlerMethod.invokeAndHandle()在调用invokeForRequest 后拿到returnValue -> returnValueHandlers.handleReturnValue() -> HandlerMethodReturnValueHandlerComposite.handleReturnValue() -> HandlerMethodReturnValueHandlerComposite.selectHandler -> RequestResponseBodyMethodProcessor.handleReturnValue()
画成图:
DispatcherServlet.doDispatch
DispatcherServlet是spring mvc的核心,在doDispatch方法中,框架从已经解析好的拦截器,controller url的handler中,根据request的url,匹配出相应的HandlerMapping,并获取了HandlerAdapter的封装——HandlerExecutionChain的实例。随后HandlerAdapter开始处理request请求。HandlerAdapter是抽象的接口,具体的实现是在AbstractHandlerMethodAdapter.handle() 方法中
AbstractHandlerMethodAdapter.handle
该方法有三个参数,HttpServletRequest, HttpServletResponse, Object handler。handler实际的类型是HandlerMethod。
HandlerMethod对象来自于前一个方法我们提到的HandlerExecutionChain实例,获取它的主要逻辑在AbstractHandlerMethodMapping的lookupHandlerMethod方法。在这个方法中,框架根据request解析出的url(也就是我们在RequestMapping注解中写的url,例如”user/login”之类的),在mappingRegistry里匹配相应的数据,mappingRegistry就是spring提前处理好的所有url与对应方法信息的映射,里边有个list叫mappingLookup,保存的就是构造好的handlerMethod对象,然后把拿到的对象排个优先级(如果有多个的话),拿到最佳匹配,返回。贴一点代码:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
//lookupPath就是restful的url
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
//...
if (!matches.isEmpty()) {
//如果有多个,排个队
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
Match bestMatch = matches.get(0);
//...
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
回到AbstractHandlerMethodAdapter中,handle方法直接调用handleInternal方法,而handleInternal方法是一个模板方法,留给了子类实现。具体的实现在RequestMappingHandleAdapter 子类中
RequestMappingHandleAdapter.handleInternal
handleInternal方法的核心逻辑,就是调用invokeHandlerMethod方法,获取了ModelAndView对象。
RequestMappingHandleAdapter.invokeHandlerMethod
先通过handlerMethod构造了ServletInvocableHandlerMethod实例,ServletInvocableHandlerMethod实例实际上就是对HandlerMethod功能的扩展,包括了对处理请求的method的封装,对returnValue的封装。是的,终于到我们今天的主题了,对returnValue的处理就是在ServletInvocableHandlerMethod中进行的。贴一点代码:
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);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
//...
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//在此处,封装好的controller方法对请求进行处理
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
ServletInvocableHandlerMethod.invokeAndHandle
在invokeAndHandle方法中,对请求的处理逻辑是在父类InvocableHandlerMethod的invokeForRequest方法中。
InvocableHandlerMethod.invokeForRequest
首先,通过request获取方法的参数。逻辑在getMethodArgumentValues方法中。逻辑也比较直接,就是解析一下
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;
}