要了解业务代码的返回值是如何被框架解析的,就得知道框架如何调用我们写的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的urlList<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;}
