• handlerAdapter与方法返回值的处理


    前提:处理器方法被调用并返回了结果
    
    public void invokeAndHandle(ServletWebRequest webRequest,
                ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    //调用处理器对应的处理器方法
            Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    //设置响应状态,如果在调用处理器方法时发生了错误,用webRequest获取response对象并设置它的状态
            setResponseStatus(webRequest);
    
            if (returnValue == null) {
                if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
    //为TRUE标识请求已经结束
                    mavContainer.setRequestHandled(true);
                    return;
                }
            }
            else if (StringUtils.hasText(this.responseReason)) {
                mavContainer.setRequestHandled(true);
                return;
            }
    
            mavContainer.setRequestHandled(false);
            try {
                this.returnValueHandlers.handleReturnValue(
                        returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
            }
            catch (Exception ex) {
                if (logger.isTraceEnabled()) {
                    logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
                }
                throw ex;
            }
        }
    
    @Override
        public void handleReturnValue(Object returnValue, MethodParameter returnType,
                ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    //遍历寻找能够处理中返回参数类型的处理器,如果没有找到就抛出异常,判断是否能够处理很简单就是调用处理器的support方法
            HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
            if (handler == null) {
                throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
            }
    //处理
            handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
        }
     
    
    现在以RequestResponseBodyMethodProcessor为例
    
    这个处理器是判断当前方法上面是否有@ResponseBody,如果有就表示这个方法能够支持处理。@Override
        public boolean supportsReturnType(MethodParameter returnType) {
    //判断这个方法对应的类是否有@ResponseBody注解
            return (AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResponseBody.class) != null ||
    //判断这个方法上是否有@ResponseBody注解
                    returnType.getMethodAnnotation(ResponseBody.class) != null);
        }
    
    
    @Override
        public void handleReturnValue(Object returnValue, MethodParameter returnType,
                ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
                throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    //设置requestHandled已经被处理
            mavContainer.setRequestHandled(true);
    
            // Try even with null return value. ResponseBodyAdvice could get involved.
            writeWithMessageConverters(returnValue, returnType, webRequest);
        }
    
    
    protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType, NativeWebRequest webRequest)
                throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    //包装request和response对象
            ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
            ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
            writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
        }
    
    
    protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType,
                ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
                throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    //获取返回值的类型
            Class<?> returnValueClass = getReturnValueType(returnValue, returnType);
    //获取返回值的泛型类型,比如List<String>,那么泛型类型就String.class
            Type returnValueType = getGenericType(returnType);
            HttpServletRequest servletRequest = inputMessage.getServletRequest();
    //获取浏览器能够接受的类型
            List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(servletRequest);
    //获取返回的类型
            List<MediaType> producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass, returnValueType);
    
            if (returnValue != null && producibleMediaTypes.isEmpty()) {
                throw new IllegalArgumentException("No converter found for return value of type: " + returnValueClass);
            }
    
            Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
            for (MediaType requestedType : requestedMediaTypes) {
                for (MediaType producibleType : producibleMediaTypes) {
    //寻找兼容的媒体类型
                    if (requestedType.isCompatibleWith(producibleType)) {
    //保存两者中认为最好的类型作为兼容类型
                        compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
                    }
                }
            }
    //如果兼容类型为空,并且方法返回值又不为空,那么抛出异常,找不到浏览器能够接受的返回类型
            if (compatibleMediaTypes.isEmpty()) {
                if (returnValue != null) {
                    throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
                }
                return;
            }
    
            List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);
    //对类型进行排序
            MediaType.sortBySpecificityAndQuality(mediaTypes);
    
            MediaType selectedMediaType = null;
            for (MediaType mediaType : mediaTypes) {
                if (mediaType.isConcrete()) {
    //如果找到不是通配符的媒体类型就作为选择的媒体类型(就是非综合类型,那种不能表示到具体是哪一种类型的,比如MediaType.ALL)
                    selectedMediaType = mediaType;
                    break;
                }
    //如果媒体类型是all(就是所有类型)或者是application应用类类型,那么设置成流类型
                else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
                    selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
                    break;
                }
            }
    
            if (selectedMediaType != null) {
    //去除权衡值,比如application/json;q=0.8,去除后变成application/json
                selectedMediaType = selectedMediaType.removeQualityValue();
    //遍历所有注册的消息转换器,这里和解析参数时是差不多的操作
                for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
                    if (messageConverter instanceof GenericHttpMessageConverter) {
                        if (((GenericHttpMessageConverter<T>) messageConverter).canWrite(returnValueType,
                                returnValueClass, selectedMediaType)) {
    //调用实现了ResponseBodyAdvice的controller,对值进行处理
                            returnValue = (T) getAdvice().beforeBodyWrite(returnValue, returnType, selectedMediaType,
                                    (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
                                    inputMessage, outputMessage);
                            if (returnValue != null) {
                                addContentDispositionHeader(inputMessage, outputMessage);
                                ((GenericHttpMessageConverter<T>) messageConverter).write(returnValue,
                                        returnValueType, selectedMediaType, outputMessage);
                                if (logger.isDebugEnabled()) {
                                    logger.debug("Written [" + returnValue + "] as "" +
                                            selectedMediaType + "" using [" + messageConverter + "]");
                                }
                            }
                            return;
                        }
                    }
                    else if (messageConverter.canWrite(returnValueClass, selectedMediaType)) {
                        returnValue = (T) getAdvice().beforeBodyWrite(returnValue, returnType, selectedMediaType,
                                (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
                                inputMessage, outputMessage);
                        if (returnValue != null) {
                            addContentDispositionHeader(inputMessage, outputMessage);
                            ((HttpMessageConverter<T>) messageConverter).write(returnValue,
                                    selectedMediaType, outputMessage);
                            if (logger.isDebugEnabled()) {
                                logger.debug("Written [" + returnValue + "] as "" +
                                        selectedMediaType + "" using [" + messageConverter + "]");
                            }
                        }
                        return;
                    }
                }
            }
    
            if (returnValue != null) {
                throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
            }
        }
    
    //遍历实现了ResponseBodyAdvice的Controller
    private <T> Object processBody(Object body, MethodParameter returnType, MediaType contentType,
                Class<? extends HttpMessageConverter<?>> converterType,
                ServerHttpRequest request, ServerHttpResponse response) {
    
            for (ResponseBodyAdvice<?> advice : getMatchingAdvice(returnType, ResponseBodyAdvice.class)) {
    //判断是否支持,最后使用outputmessage写出到客户端
                if (advice.supports(returnType, converterType)) {
                    body = ((ResponseBodyAdvice<T>) advice).beforeBodyWrite((T) body, returnType,
                            contentType, converterType, request, response);
                }
            }
            return body;
        }
    
    
    处理完后返回。
    
    再来看看ModelAndViewMethodReturnValueHandler是这么处理返回值的
    
    @Override
        public boolean supportsReturnType(MethodParameter returnType) {
    //处理返回类型是ModelAndView的方法
            return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
        }
    
    
    @Override
        public void handleReturnValue(Object returnValue, MethodParameter returnType,
                ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    //如果返回值为空,那么设置请求结束
            if (returnValue == null) {
                mavContainer.setRequestHandled(true);
                return;
            }
    
            ModelAndView mav = (ModelAndView) returnValue;
    //判断视图名是否为String类型
            if (mav.isReference()) {
    //获取视图名称
                String viewName = mav.getViewName();
    //给视图容器设置视图名称
                mavContainer.setViewName(viewName);
    //如果是重定向视图名称,那么设置重定向表示为true,如果没有重新配置这个handler,并且设置自定的标识符,默认就是判断这个视图名是否以redirect:开头
                if (viewName != null && isRedirectViewName(viewName)) {
                    mavContainer.setRedirectModelScenario(true);
                }
            }
            else {
    //如果视图不是String类型的,那么就是视图对象
                View view = mav.getView();
                mavContainer.setView(view);
    //判断是否是smartView,如果是,再判断是否是重定向视图,如果是就设置重定向标识
                if (view instanceof SmartView) {
                    if (((SmartView) view).isRedirectView()) {
                        mavContainer.setRedirectModelScenario(true);
                    }
                }
            }
    //将ModelAndView中的属性全部存到ModelAndViewContainer中
            mavContainer.addAllAttributes(mav.getModel());
        }
    
    
    
    接着调用了以下方法
    
    getModelAndView(mavContainer, modelFactory, webRequest);
    
    private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
                ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
    //更新Model
            modelFactory.updateModel(webRequest, mavContainer);
    //如果是json处理方式的,那么这个request已经处理完了,返回null,如果是其他的,需要视图解析的,那么继续。
            if (mavContainer.isRequestHandled()) {
                return null;
            }
    //获取model,会根据标识来判断是返回默认model还是重定向model
            ModelMap model = mavContainer.getModel();
            ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
            if (!mavContainer.isViewReference()) {
                mav.setView((View) mavContainer.getView());
            }
            if (model instanceof RedirectAttributes) {
                Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
                HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
                RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
            }
            return mav;
        }
    
    
    一下是获取model的方法
    public ModelMap getModel() {
            if (useDefaultModel()) {
                return this.defaultModel;
            }
            else {
                if (this.redirectModel == null) {
                    this.redirectModel = new ModelMap();
                }
                return this.redirectModel;
            }
        }
    
        /**
         * Whether to use the default model or the redirect model.
         */
        private boolean useDefaultModel() {
            return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));
        }
    
    
    更新Model
    public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception {
    //获取默认的Model,sessionAttribute注释的属性,只有model和modelMap才能用,redirectModel没有用。
            ModelMap defaultModel = container.getDefaultModel();
    //检查是否已经被清理了,在handlerMethod中可以调用SessionStatus的setComplete方法用于标识要清理数据
            if (container.getSessionStatus().isComplete()){
                this.sessionAttributesHandler.cleanupAttributes(request);
            }
            else {
    //如果没有被清理就将注解中定义要保存的key存到session中
    //储存sessionAttribute,这个功能可以用来做缓存
                this.sessionAttributesHandler.storeAttributes(request, defaultModel);
            }
    //如果请求没有结束,并且获取的model是默认的model,那么就更新绑定结果
            if (!container.isRequestHandled() && container.getModel() == defaultModel) {
                updateBindingResult(request, defaultModel);
            }
        }
    
    
    public void storeAttributes(WebRequest request, Map<String, ?> attributes) {
            for (String name : attributes.keySet()) {
                Object value = attributes.get(name);
                Class<?> attrType = (value != null) ? value.getClass() : null;
    //判断是否是@SessionAttribute定义的属性名和类型,如果是就存到session中
                if (isHandlerSessionAttribute(name, attrType)) {
                    this.sessionAttributeStore.storeAttribute(request, name, value);
                }
            }
        }
    
    public void storeAttribute(WebRequest request, String attributeName, Object attributeValue) {
            Assert.notNull(request, "WebRequest must not be null");
            Assert.notNull(attributeName, "Attribute name must not be null");
            Assert.notNull(attributeValue, "Attribute value must not be null");
            String storeAttributeName = getAttributeNameInSession(request, attributeName);
            request.setAttribute(storeAttributeName, attributeValue, WebRequest.SCOPE_SESSION);
        }
    
    //更新绑定结果
    private void updateBindingResult(NativeWebRequest request, ModelMap model) throws Exception {
            List<String> keyNames = new ArrayList<String>(model.keySet());
            for (String name : keyNames) {
                Object value = model.get(name);
    //将绑定结果,比如校验的结果,用于页面展示
                if (isBindingCandidate(name, value)) {
                    String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;
    
                    if (!model.containsAttribute(bindingResultKey)) {
                        WebDataBinder dataBinder = this.dataBinderFactory.createBinder(request, value, name);
                        model.put(bindingResultKey, dataBinder.getBindingResult());
                    }
                }
            }
        }
    
    
    回到DispatcherServlet
    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.
                    mappedHandler = getHandler(processedRequest);
                    if (mappedHandler == null || mappedHandler.getHandler() == null) {
                        noHandlerFound(processedRequest, response);
                        return;
                    }
    
                    // Determine handler adapter for the current request.
                    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
                    // Process last-modified header, if supported by the handler.
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if (logger.isDebugEnabled()) {
                            logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                        }
                        if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
    //调用拦截器的preHandler方法
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
    
                    // Actually invoke the handler.
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
    //如果没有设置视图,那么使用viewNameTranslator获取默认的视图名
                    applyDefaultViewName(processedRequest, mv);
    //调用拦截器的postHandle方法
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                }
                catch (Exception ex) {
                    dispatchException = ex;
                }
    //处理结果
                processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
            }
            catch (Exception ex) {
    //如果出现异常就触发拦截器的afterCompletion方法
                triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
            }
            catch (Error err) {
    //如果出现异常就触发拦截器的afterCompletion方法
                triggerAfterCompletionWithError(processedRequest, response, mappedHandler, 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);
                    }
                }
            }
        }
    
    
    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 {
    //获取handler
                    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.isDebugEnabled()) {
                    logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                            "': assuming HandlerAdapter completed request handling");
                }
            }
    
            if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                // Concurrent handling started during a forward
                return;
            }
    
            if (mappedHandler != null) {
                mappedHandler.triggerAfterCompletion(request, response, null);
            }
        }
    
    //渲染方法
    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
            // Determine locale for request and apply it to the response.
    //从request中获取本地信息
            Locale locale = this.localeResolver.resolveLocale(request);
            response.setLocale(locale);
    
            View view;
    //判断mv中设置的视图是视图名称还是视图对象
            if (mv.isReference()) {
                // We need to resolve the view name.如果是视图名称,那么通过视图解析器解析获取视图对象{(protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
                HttpServletRequest request) throws Exception {
    
            for (ViewResolver viewResolver : this.viewResolvers) {
                View view = viewResolver.resolveViewName(viewName, locale);
                if (view != null) {
                    return view;
                }
            }
            return null;
        }))
                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 {
                // 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.isDebugEnabled()) {
                logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
            }
            try {
    //调用视图的渲染方法mv.getModelInternal()获取modelMap,我们的设置的参数在里面
                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;
            }
        }
    
    
    InternalResourceViewResolver解析器(jstl)
    
    protected AbstractUrlBasedView buildView(String viewName) throws Exception {
            AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
            view.setUrl(getPrefix() + viewName + getSuffix());
    
            String contentType = getContentType();
            if (contentType != null) {
                view.setContentType(contentType);
            }
    
            view.setRequestContextAttribute(getRequestContextAttribute());
            view.setAttributesMap(getAttributesMap());
    
            Boolean exposePathVariables = getExposePathVariables();
            if (exposePathVariables != null) {
                view.setExposePathVariables(exposePathVariables);
            }
            Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
            if (exposeContextBeansAsAttributes != null) {
                view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
            }
            String[] exposedContextBeanNames = getExposedContextBeanNames();
            if (exposedContextBeanNames != null) {
                view.setExposedContextBeanNames(exposedContextBeanNames);
            }
    
            return view;
        }
    
    //合并属性
    protected Map<String, Object> createMergedOutputModel(Map<String, ?> model, HttpServletRequest request,
                HttpServletResponse response) {
    
            @SuppressWarnings("unchecked")
    //判断是否要暴露路径变量供视图使用
            Map<String, Object> pathVars = (this.exposePathVariables ?
                    (Map<String, Object>) request.getAttribute(View.PATH_VARIABLES) : null);
    
            // Consolidate static and dynamic model attributes.
            int size = this.staticAttributes.size();
            size += (model != null ? model.size() : 0);
            size += (pathVars != null ? pathVars.size() : 0);
    
            Map<String, Object> mergedModel = new LinkedHashMap<String, Object>(size);
            mergedModel.putAll(this.staticAttributes);
    //将暴露的属性合并到modelMap中
            if (pathVars != null) {
                mergedModel.putAll(pathVars);
            }
            if (model != null) {
                mergedModel.putAll(model);
            }
    
            // Expose RequestContext?
            if (this.requestContextAttribute != null) {
                mergedModel.put(this.requestContextAttribute, createRequestContext(request, response, mergedModel));
            }
    //将合并的数据返回
            return mergedModel;
        }
     
    
    
    //渲染合并的model
    protected void renderMergedOutputModel(
                Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
    
            // Expose the model object as request attributes.就是将属性设置到request,如果属性对应的值为空,那么就从request中移除
            exposeModelAsRequestAttributes(model, request);
    
            // Expose helpers as request attributes, if any.
            exposeHelpers(request);
    //如果路径和当前请求的路径一样,那么就抛出错误
            // Determine the path for the request dispatcher.
            String dispatcherPath = prepareForRendering(request, response);
    //获取请求分发器,request.getDispatcher(diapatcherPath)
            // Obtain a RequestDispatcher for the target resource (typically a JSP).
            RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
            if (rd == null) {
                throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
                        "]: Check that the corresponding file exists within your web application archive!");
            }
    
            // If already included or response already committed, perform include, else forward.
            if (useInclude(request, response)) {
                response.setContentType(getContentType());
                if (logger.isDebugEnabled()) {
                    logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
                }
                rd.include(request, response);
            }
    
            else {
                // Note: The forwarded resource is supposed to determine the content type itself.
                if (logger.isDebugEnabled()) {
                    logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
                }
    请求转发
                rd.forward(request, response);
            }
        }
  • 相关阅读:
    new和base的语法
    js常用代码
    无法识别connectionStrings
    DataTable
    字符串的操作时间格式化
    Facade外观模式(转载cnblogs)
    ArrayList下的循环绑定和循环删除
    自定义属性与事件委托相结合的实例
    泛型 开放类型和构造类型(基础学习)
    C#策略模式 摘自jspcool
  • 原文地址:https://www.cnblogs.com/honger/p/9516930.html
Copyright © 2020-2023  润新知