Spring 学习笔记

Spring MVC URL映射 学习(下)

2018-03-11  本文已影响31人  jwfy

接收到一个新的请求之后,spring就会去根据请求的URL信息选择具体的代码块去执行操作,如图就是接收到一个新的请求调用图,从Tomcat开始直到把请求分发到spring中,最后到了doDispatch方法


image

本篇学习笔记主要就是讲一个新的请求被分发到spring中spring如何处理,至于如何扫描包中的controller,得到URL配置信息可以看Spring MVC URL映射 学习(上),而本篇主要介绍了

让我们更加清楚的知道spring中一般的方法是如何确定调用的具体方法的,适配不同的模板引擎,达到渲染的地步,再者有时候又是API一般只需要返回json结构的数据,其背后的原理是如何实现的,以及我们在使用过程中如何避免出现的各种问题。

doDispatch 方法

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;
        // 初始化设置模板为null
        Exception dispatchException = null;
        // 初始化异常为null

        try {
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);
            // 文件上传的相关设置和操作

            mappedHandler = getHandler(processedRequest);
            // 根据request获取对应的请求执行链
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
               // 如果没有对应的handler对于,则应该是定义为404,通过noHandlerFound确认
                noHandlerFound(processedRequest, response);
                return;
            }

            // 通过handler获得合适的handler适配器
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
               // 是get方法或者head方法
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                // 如果handler 是LastModified类,则获取其lastModified值,否则返回-1
                // lastModify 是spring添加了缓存机制,当重复请求同样的内容,返回403,而不会返回真正的内容,具体可看下面的LastModified机制这一小节
                if (logger.isDebugEnabled()) {
                    logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                }
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }

         // 前置的handler预处理,就是获取执行链的拦截器,对请求进行拦截处理
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
               // 如果拦截器拦截成功,返回false,直接结束了
               // 当然在这其中拦截器肯定需要特定返回自身的内容到response中,便于展示在页面上
               // 不过从页面角度出发并没有非常实质性的拦截器处理,这点存疑?
                return;
            }

            // 真正的调用各自的执行方法,返回ModelAndView后续在invoke这一小节细说
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            applyDefaultViewName(processedRequest, mv);
            // 如果mv没有包含有效的视图,则从dispatch的viewNameTranslator属性上获取对应的默认视图
            mappedHandler.applyPostHandle(processedRequest, response, mv);
            // 拦截器的后置处理
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        
        // 到这里了可能存在异常,类似的500请求处理就是在这里完成操作的
        // 同时包含了数据回填到response中的操作
        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);
            }
        }
    }
}

获取执行链

AbstractHandlerMapping 类

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    for (HandlerMapping hm : this.handlerMappings) {
       // 这个handlerMappings就是在扫描URL得到的URL容器信息
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    return null;
}

如图所示就是


image

getHandler 方法

AbstractHandlerMapping 类

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    Object handler = getHandlerInternal(request);
    // 调用子类实现的具体方法,获得执行链对象
    if (handler == null) {
        handler = getDefaultHandler();
    }
    if (handler == null) {
       // 到这里意味着真的找不到可用的执行链对象
        return null;
    }
    
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = getApplicationContext().getBean(handlerName);
    }

    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    // 重新生成一个执行链对象,可能会和handler是同一个对象
    
    if (CorsUtils.isCorsRequest(request)) {
       // 查看request头部信息是否包含了Origin信息,如果有则判断成功
       // cors跨域设置
        CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
        // 获取全局的跨域配置
        CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
        CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
        // 结合当前handler和全局配置组合成一个新的跨域配置
        executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        // 重新生成可用的执行链对象
    }
    return executionChain;
}

AbstractUrlHandlerMapping类

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    // 从request中获取到URL信息
    Object handler = lookupHandler(lookupPath, request);
    // 得到执行链路的对象,一般为HandlerExecutionChain
    if (handler == null) {
        // 如果没有找到,则匹配一些特殊的请求,尽可能的匹配清楚
        Object rawHandler = null;
        if ("/".equals(lookupPath)) {
           // 根路径的handler
            rawHandler = getRootHandler();
        }
        if (rawHandler == null) {
           // 默认路径的handler
            rawHandler = getDefaultHandler();
        }
        if (rawHandler != null) {
            // 根路径或者默认路径中的一个不为null
            if (rawHandler instanceof String) {
                String handlerName = (String) rawHandler;
                rawHandler = getApplicationContext().getBean(handlerName);
            }
            validateHandler(rawHandler, request);
            handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
            // 同样的套路得到新的执行链路对象信息
        }
    }
    if (handler != null && logger.isDebugEnabled()) {
        logger.debug("Mapping [" + lookupPath + "] to " + handler);
        // 打个日志,表示匹配到了相关的bean信息
    }
    else if (handler == null && logger.isTraceEnabled()) {
        logger.trace("No handler mapping found for [" + lookupPath + "]");
    }
    return handler;
}
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
    // 从hanlerMap中获取该URL对应的controller
    Object handler = this.handlerMap.get(urlPath);
    if (handler != null) {
        // 
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = getApplicationContext().getBean(handlerName);
        }
        validateHandler(handler, request);
        return buildPathExposingHandler(handler, urlPath, urlPath, null);
    }

    // 没有完全匹配,采取模糊匹配,正则还是?
    // 题外话,python的tornado和Django都是采用的正则匹配的
    List<String> matchingPatterns = new ArrayList<String>();
    for (String registeredPattern : this.handlerMap.keySet()) {
        if (getPathMatcher().match(registeredPattern, urlPath)) {
           // getPathMatcher() 返回的就是AntPathMatcher对象
           // 同样也是按照getPathMatcher的匹配规则去匹配的
            matchingPatterns.add(registeredPattern);
        }
        else if (useTrailingSlashMatch()) {
            if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
                matchingPatterns.add(registeredPattern +"/");
            }
        }
    }
    // 总之最后会得到一个list,包含了匹配中的URL信息

    String bestMatch = null;
    Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
    if (!matchingPatterns.isEmpty()) {
        Collections.sort(matchingPatterns, patternComparator);
        // 模糊匹配的集合不为空,则按照urlPath定义的排序规则去重排序
        if (logger.isDebugEnabled()) {
            logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
        }
        bestMatch = matchingPatterns.get(0);
        // 第一个是最佳的匹配路径
    }
    if (bestMatch != null) {
       // 接下来的一切都是依照这个bestMatch不为null,否则就直接返回null,表示没有合适的匹配handler
        handler = this.handlerMap.get(bestMatch);
        if (handler == null) {
           // 如果容器中不存在该handler
            if (bestMatch.endsWith("/")) {
                handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
            }
            // 对最佳的URL路径处理之后,还是不在对应的handler,则提示没有具体的handler信息
            if (handler == null) {
                throw new IllegalStateException(
                        "Could not find handler for best pattern match [" + bestMatch + "]");
            }
        }
        // 包含了对应的handler信息了
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = getApplicationContext().getBean(handlerName);
            // 如果是string,则通过spring的容器获取对应的bean(一般是controller)
        }
        validateHandler(handler, request);
        String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);

        // 
        Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
        for (String matchingPattern : matchingPatterns) {
            if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
                Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
                Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
                uriTemplateVariables.putAll(decodedVars);
            }
        }
        if (logger.isDebugEnabled()) {
            logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
        }
        
        // 生成执行链路对象HandlerExecutionChain
        return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
    }

    // 什么都没有发现,返回null
    return null;
}
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
        String pathWithinMapping, Map<String, String> uriTemplateVariables) {

    HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
    // 创建执行链路的对象chain
    chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
    // 添加PathExposingHandlerInterceptor拦截器
    if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
        chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
    }
    return chain;
}

如图就是调用buildPathExposingHandler之后返回的执行链对象chain


image

404状态码

在没找到合适的handler执行链的时候,就会进入该方法(其实就是404了)

protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
    if (pageNotFoundLogger.isWarnEnabled()) {
       // 类似这种代码其实是为了适配不同的日志系统
        pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + getRequestUri(request) +
                "] in DispatcherServlet with name '" + getServletName() + "'");
    }
    if (this.throwExceptionIfNoHandlerFound) {
        throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
                new ServletServerHttpRequest(request).getHeaders());
    }
    else {
       // HttpServletResponse.SC_NOT_FOUND 就是404
       // 调用的是Tomcat本身的404设置页面返回的,可以在web.xml 中配置自定义的404页面
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
    }
}

如图就是采用了jdk14Logger打印出来的数据,当然了现实项目中更多的应用log4j、log4j2


image

获取适配器

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    for (HandlerAdapter ha : this.handlerAdapters) {
       // handlerAdapters的值就是在initStrategies()方法中获取到的
       // 默认的适配器是三个   
       // org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter
       // org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
       // org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
       // 如果细看适配器的初始化过程会发现,spring会优先获取用户自定义的适配器,如果没有才会默认使用这三个适配器
        if (ha.supports(handler)) {
            return ha;
        }
    }
    throw new ServletException("No adapter for handler [" + handler +
            "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
 // 常出现的没有找到适配器的报错的地方就是这里,遍历了所有的适配器并没有符合handler的适配器,则提示没有适配器
}

接下来看看三种适配器到底是如何判断对象是符合自身的要求的

HttpRequestHandlerAdapter

@Override
public boolean supports(Object handler) {
    return (handler instanceof HttpRequestHandler);
    // 是否是HttpRequestHandler对象
}

SimpleControllerHandlerAdapter

@Override
public boolean supports(Object handler) {
    return (handler instanceof Controller);
    // 是否是Controller对象
}

AnnotationMethodHandlerAdapter

@Override
public boolean supports(Object handler) {
    return getMethodResolver(handler).hasHandlerMethods();
    // 获取到方法处理器,再判断其是否为空
}

private ServletHandlerMethodResolver getMethodResolver(Object handler) {
    Class<?> handlerClass = ClassUtils.getUserClass(handler);
    ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);
    if (resolver == null) {
        synchronized (this.methodResolverCache) {
            resolver = this.methodResolverCache.get(handlerClass);
            if (resolver == null) {
                resolver = new ServletHandlerMethodResolver(handlerClass);
                this.methodResolverCache.put(handlerClass, resolver);
            }
        }
    }
    return resolver;
}

LastModified 机制

组合成为一个ServletWebRequest对象后就调用该方法,确认lastModified

public boolean checkNotModified(String etag, long lastModifiedTimestamp) {
    HttpServletResponse response = getResponse();
    if (this.notModified || !isStatusOK(response)) {
       // 不需要modified机制或者 response为null或者无效更或者状态码不是200
        return this.notModified;
    }

    if (validateIfUnmodifiedSince(lastModifiedTimestamp)) {
    // 其对象会存储该请求的时间,验证是否符合lastModified的条件
        if (this.notModified) {
            response.setStatus(HttpStatus.PRECONDITION_FAILED.value());
        }
        return this.notModified;
    }

    boolean validated = validateIfNoneMatch(etag);
    // 默认传过来的etag是null,返回false

    if (!validated) {
        validateIfModifiedSince(lastModifiedTimestamp);
        // 看是否是第一次来,如果是就需要刷新其时间
    }

    boolean isHttpGetOrHead = SAFE_METHODS.contains(getRequest().getMethod());
    // 是否为get或者head请求,并且变更response的头信息
    if (this.notModified) {
        response.setStatus(isHttpGetOrHead ?
                HttpStatus.NOT_MODIFIED.value() : HttpStatus.PRECONDITION_FAILED.value());
    }
    if (isHttpGetOrHead) {
        if(lastModifiedTimestamp > 0 && isHeaderAbsent(response, LAST_MODIFIED)) {
            response.setDateHeader(LAST_MODIFIED, lastModifiedTimestamp);
        }
        if (StringUtils.hasLength(etag) && isHeaderAbsent(response, ETAG)) {
            response.setHeader(ETAG, padEtagIfNecessary(etag));
        }
    }
   // 如果符合其机制,就返回true,否则返回false
    return this.notModified;
}

invoke调用

只是取了这么个名字,在获取到了合适的执行链,有了合适的适配器,有没有被拦截器处理,最后来到了真正调用方法的地方了。

上文可知,这是由各自的适配器调用handler方法来获取真正的内容了。

在没看源码前,其实我们应该能够猜到一些细节,例如

SimpleControllerHandlerAdapter

HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter是一样的,就只解释一种了。

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
    return ((Controller) handler).handleRequest(request, response);
    // 果然不出我们期望的,就是controller接口实现的方法handleRequest去完成真正的调用
    // 如下面的demo,就是调用该方法,不过此方法意味着一个类只有一个URL映射
    // 不像注解那样一个类中可以包含多种URL映射
}
public class NameController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView modelAndView=new ModelAndView();

        modelAndView.setViewName("/page/404");

        return modelAndView;
    }
}

AnnotationMethodHandlerAdapter

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {

    Class<?> clazz = ClassUtils.getUserClass(handler);
    Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
    if (annotatedWithSessionAttributes == null) {
        annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
        this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
    }
    
    if (annotatedWithSessionAttributes) {
        checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
    }
    else {
        checkAndPrepare(request, response, true);
    }

    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                return invokeHandlerMethod(request, response, handler);
            }
        }
    }
   // 前面各种操作来到了inVokeMethod
    return invokeHandlerMethod(request, response, handler);
}

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {

    ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
    Method handlerMethod = methodResolver.resolveHandlerMethod(request);
    // 处理request,得到对应的需要被执行的方法
    // 在这个函数中还有类URL注解信息和方法注解信息的拼接匹配过程
    // 返回的对象就是一个Method对象
    ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    ExtendedModelMap implicitModel = new BindingAwareModelMap();

    Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
    // 这个是真正的调用过程,其中还包含了请求参数的处理等各种过程
    // 返回的结果可以是字符串也可以是mv,看具体的方法是如何实现的
    ModelAndView mav =
            methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
    // 获取具体的视图类,如果是具体业务返回,视图为null,具体的数据已经通过response的body回填返回了
    methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
    // 视图属性更新
    return mav;
}
image
    public ModelAndView getModelAndView(Method handlerMethod, Class<?> handlerType, Object returnValue,
            ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception {

        ResponseStatus responseStatus = AnnotatedElementUtils.findMergedAnnotation(handlerMethod, ResponseStatus.class);
        // 查看该方法是否加了ResponseStatus注解,主要是为了替换状态码
        if (responseStatus != null) {
            HttpStatus statusCode = responseStatus.code();
            String reason = responseStatus.reason();
            // 该执行方法存在该ResponseStatus的注解,获取状态码和原因
            // 不过该具体使用的时候,只能是HttpStatus枚举类中的选项,默认是500错误
            if (!StringUtils.hasText(reason)) {
               // 没有具体原因,就只设置状态码,否则就返回错误码
                webRequest.getResponse().setStatus(statusCode.value());
            }
            else {
                webRequest.getResponse().sendError(statusCode.value(), reason);
            }
            // 设置当前的请求属性,名字为模板和状态,值就是状态码
            webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, statusCode);
            this.responseArgumentUsed = true;
        }

        // 如果存在自定义的ModelAndView解析
        if (customModelAndViewResolvers != null) {
            for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) {
                ModelAndView mav = mavResolver.resolveModelAndView(
                        handlerMethod, handlerType, returnValue, implicitModel, webRequest);
                // 遍历自定义ModelAndView解析器,选择合适的mv,并且不是初始化的视图类
                // 这个是由用户自定义的实现类,并且切记记得适配器的设置,别没弄好导致无合适的适配器的错误
                // ModelAndView UNRESOLVED = new ModelAndView();
                if (mav != ModelAndViewResolver.UNRESOLVED) {
                    return mav;
                }
            }
        }
        
        // 返回的类型是HttpEntity、string、ModelAndView、Model、View、Map
        if (returnValue instanceof HttpEntity) {
           // 返回值是HttpEntity类型的数据,不需要合适的视图
           // 更多的是返回json数据
            handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest);
            return null;
        }
        else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
           // 方法包含了ResponseBody注解,直接把returnValue 塞入response
            handleResponseBody(returnValue, webRequest);
            return null;
        }
        else if (returnValue instanceof ModelAndView) {
            ModelAndView mav = (ModelAndView) returnValue;
            mav.getModelMap().mergeAttributes(implicitModel);
            return mav;
        }
        else if (returnValue instanceof Model) {
            return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
        }
        else if (returnValue instanceof View) {
            return new ModelAndView((View) returnValue).addAllObjects(implicitModel);
        }
        else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {
            addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
            return new ModelAndView().addAllObjects(implicitModel);
        }
        else if (returnValue instanceof Map) {
            return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map<String, ?>) returnValue);
        }
        else if (returnValue instanceof String) {
            return new ModelAndView((String) returnValue).addAllObjects(implicitModel);
        }
        else if (returnValue == null) {
            // Either returned null or was 'void' return.
            if (this.responseArgumentUsed || webRequest.isNotModified()) {
                return null;
            }
            else {
                // Assuming view name translation...
                return new ModelAndView().addAllObjects(implicitModel);
            }
        }
        else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
            // Assume a single model attribute...
            addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
            return new ModelAndView().addAllObjects(implicitModel);
        }
        else {
            throw new IllegalArgumentException("Invalid handler method return value: " + returnValue);
        }
    }

在这其中的代码快customModelAndViewResolvers,更多的细节可以看看Spring Controller层记录日志配置的实践demo

视图渲染

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
        HandlerExecutionChain mappedHandler, ModelAndView mv, 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);
            // 异常处理返回的mv,异常mv配置存放在handlerExceptionResolvers
            errorView = (mv != null);
        }
    }

    // 有可用的mv
    if (mv != null && !mv.wasCleared()) {
        render(mv, request, response);
        // render就是渲染,回填数据到response中去
        if (errorView) {
           // 如果有非ModelAndViewDefiningException,则清除请求的属性信息
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    else {
    // 没有可用的视图信息
    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }

    if (mappedHandler != null) {
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

渲染render

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.resolveLocale(request);
    response.setLocale(locale);

    View view;
    if (mv.isReference()) {
        // mv的视图名字是string类型,则需要找到真正的视图
        // 需要使用到local字段信息
        view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
        if (view == null) {
           // 没有找到有效的视图,报错提示
            throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                    "' in servlet with name '" + getServletName() + "'");
        }
    }
    else {
       // 否则就被认为是View对象,直接获取就可以了
        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.isDebugEnabled()) {
        logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
    }
    try {
        if (mv.getStatus() != null) {
           // 从mv中获取http状态码
            response.setStatus(mv.getStatus().value());
        }
        // 来到了真正的渲染,所有的数据已经准备就绪
        view.render(mv.getModelInternal(), request, response);
    }
    catch (Exception ex) {
        if (logger.isDebugEnabled()) {
            logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
                    getServletName() + "'", ex);
        }
        throw ex;
    }
}

其中的resolve

View 渲染

View本身是一个接口类,spring根据不同的业务实现了多个不同的View实体类,包含了html、pdf、Excel等。

AbstractView 抽象视图类

public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
    Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
    prepareResponse(request, response);
    renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}

由各自的子类去实现renderMergedOutputModel方法

image
上一篇下一篇

猜你喜欢

热点阅读