SpringMVC源码剖析

2023-03-05  本文已影响0人  王侦

1.父子容器的创建(一般只创建DispatcherServlet中的子容器)

<web-app>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring.xml</param-value>
    </context-param>

    <servlet>
        <servlet-name>app</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>

</web-app>

父容器的创建

ContextLoaderListener的作用是用来创建一个Spring容器,就是我们说的SpringMVC父子容器中的父容器,执行流程为:

子容器的创建

Tomcat启动过程中,执行完ContextLoaderListener的contextInitialized()之后,就会创建DispatcherServlet了,web.xml中定义DispatcherServlet时,load-on-startup为1,表示在Tomcat启动过程中要把这个DispatcherServlet创建并初始化出来,而这个过程是比较费时间的,所以要把load-on-startup设置为1,如果不为1,会在servlet接收到请求时才来创建和初始化,这样会导致请求处理比较慢。

2.DispatcherServlet初始化逻辑

DispatcherServlet#init()实际是父类HttpServletBean#init。

    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
    org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
    org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
    org.springframework.web.servlet.function.support.HandlerFunctionAdapter


org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
    org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
    org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

2.1 四种Handler

在SpringMVC中有四种Handler:

2.2 HandlerMappings初始化

DispatcherServlet#initHandlerMappings

可得HandlerMapping:

DispatcherServlet#createDefaultStrategy

    protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
        return context.getAutowireCapableBeanFactory().createBean(clazz);
    }

这三个对象是通过createBean()创建的,所以有Bean的生命周期动作,包括调用初始化方法。
比如RequestMappingHandlerMapping#afterPropertiesSet负责处理@Controller和@RequestMapping的方法
解析出来有两个map:pathLookup<path, mapping注解信息>以及registry<mapping注解, method>

RequestMappingHandlerMapping#afterPropertiesSet

RequestMappingHandlerMapping#afterPropertiesSet

    class MappingRegistry {

        private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

        private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();

        private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

        private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();

        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

2.3 HandlerAdapter初始化

SpringMVC采用适配模式,把不同种类的Handler适配成一个HandlerAdapter,后续再执行HandlerAdapter的handle()方法就能执行不同种类Hanlder对应的方法。

DispatcherServlet#initHandlerAdapters从DispatcherServlet.properties中获得的适配器:

同理适配器对象也是通过createBean()创建的,所以有Bean的生命周期动作,包括调用初始化方法。

重点看一下RequestMappingHandlerAdapter#afterPropertiesSet

    public void afterPropertiesSet() {
        // Do this first, it may add ResponseBody advice beans
        initControllerAdviceCache();
        // 参数解析器
        if (this.argumentResolvers == null) {
            List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
            this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
        }//数据绑定器参数解析器
        if (this.initBinderArgumentResolvers == null) {
            List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
            this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
        }// 返回值处理器
        if (this.returnValueHandlers == null) {
            List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
            this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
        }
    }

默认的参数解析器:

    private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);

        // Annotation-based argument resolution
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
        resolvers.add(new RequestParamMapMethodArgumentResolver());
        resolvers.add(new PathVariableMethodArgumentResolver());
        resolvers.add(new PathVariableMapMethodArgumentResolver());
        resolvers.add(new MatrixVariableMethodArgumentResolver());
        resolvers.add(new MatrixVariableMapMethodArgumentResolver());
        resolvers.add(new ServletModelAttributeMethodProcessor(false));
        resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
        resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
        resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
        resolvers.add(new RequestHeaderMapMethodArgumentResolver());
        resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
        resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
        resolvers.add(new SessionAttributeMethodArgumentResolver());
        resolvers.add(new RequestAttributeMethodArgumentResolver());

        // Type-based argument resolution
        resolvers.add(new ServletRequestMethodArgumentResolver());
        resolvers.add(new ServletResponseMethodArgumentResolver());
        resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
        resolvers.add(new RedirectAttributesMethodArgumentResolver());
        resolvers.add(new ModelMethodProcessor());
        resolvers.add(new MapMethodProcessor());
        resolvers.add(new ErrorsMethodArgumentResolver());
        resolvers.add(new SessionStatusMethodArgumentResolver());
        resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
        if (KotlinDetector.isKotlinPresent()) {
            resolvers.add(new ContinuationHandlerMethodArgumentResolver());
        }

        // Custom arguments
        if (getCustomArgumentResolvers() != null) {
            resolvers.addAll(getCustomArgumentResolvers());
        }

        // Catch-all
        resolvers.add(new PrincipalMethodArgumentResolver());
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
        resolvers.add(new ServletModelAttributeMethodProcessor(true));

        return resolvers;
    }

默认的数据绑定解析器

    private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(20);

        // Annotation-based argument resolution
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
        resolvers.add(new RequestParamMapMethodArgumentResolver());
        resolvers.add(new PathVariableMethodArgumentResolver());
        resolvers.add(new PathVariableMapMethodArgumentResolver());
        resolvers.add(new MatrixVariableMethodArgumentResolver());
        resolvers.add(new MatrixVariableMapMethodArgumentResolver());
        resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
        resolvers.add(new SessionAttributeMethodArgumentResolver());
        resolvers.add(new RequestAttributeMethodArgumentResolver());

        // Type-based argument resolution
        resolvers.add(new ServletRequestMethodArgumentResolver());
        resolvers.add(new ServletResponseMethodArgumentResolver());

        // Custom arguments
        if (getCustomArgumentResolvers() != null) {
            resolvers.addAll(getCustomArgumentResolvers());
        }

        // Catch-all
        resolvers.add(new PrincipalMethodArgumentResolver());
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));

        return resolvers;
    }

默认的返回值处理器

    private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
        List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);

        // Single-purpose return value types
        handlers.add(new ModelAndViewMethodReturnValueHandler());
        handlers.add(new ModelMethodProcessor());
        handlers.add(new ViewMethodReturnValueHandler());
        handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
                this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
        handlers.add(new StreamingResponseBodyReturnValueHandler());
        handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
                this.contentNegotiationManager, this.requestResponseBodyAdvice));
        handlers.add(new HttpHeadersReturnValueHandler());
        handlers.add(new CallableMethodReturnValueHandler());
        handlers.add(new DeferredResultMethodReturnValueHandler());
        handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

        // Annotation-based return value types
        handlers.add(new ServletModelAttributeMethodProcessor(false));
        handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
                this.contentNegotiationManager, this.requestResponseBodyAdvice));

        // Multi-purpose return value types
        handlers.add(new ViewNameMethodReturnValueHandler());
        handlers.add(new MapMethodProcessor());

        // Custom return value types [可扩展]
        if (getCustomReturnValueHandlers() != null) {
            handlers.addAll(getCustomReturnValueHandlers());
        }

        // Catch-all
        if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
            handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
        }
        else {
            handlers.add(new ServletModelAttributeMethodProcessor(true));
        }

        return handlers;
    }

3.请求处理逻辑

DispatcherServlet父类HttpServlet#service

    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);

                // 进行映射
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 找到最合适的HandlerAdapter
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.  HTTP缓存相关
                String method = request.getMethod();
                boolean isGet = HttpMethod.GET.matches(method);
                if (isGet || HttpMethod.HEAD.matches(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                // 前置拦截器
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    // 返回false就不进行后续处理了
                    return;
                }

                // Actually invoke the handler.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                // 如果mv有  视图没有,给你设置默认视图
                applyDefaultViewName(processedRequest, mv);
                //后置拦截器
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                // As of 4.3, we're processing Errors thrown from handler methods as well,
                // making them available for @ExceptionHandler methods and other scenarios.
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            // 渲染视图
            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);
                }
            }
        }
    }

DispatcherServlet#doDispatch:

3.1 查找HandlerMapping

AbstractHandlerMapping会负责调用子类的getHandlerInternal(HttpServletRequest request)方法从而找到请求对应的Handler,然后AbstractHandlerMapping负责将Handler和应用中所配置的HandlerInterceptor整合成为一个HandlerExecutionChain对象。

查找优先级是按照配置的顺序:

3.2 转换为HandlerAdapter

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        for (HandlerAdapter adapter : this.handlerAdapters) {
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
    }
    throw new ServletException("No adapter for handler [" + handler +
                               "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

适配逻辑:

public boolean supports(Object handler) {
    return (handler instanceof HttpRequestHandler);
}

public boolean supports(Object handler) {
    return (handler instanceof Controller);
}

public final boolean supports(Object handler) {
    return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

public boolean supports(Object handler) {
    return handler instanceof HandlerFunction;
}

3.3 HandlerAdapter#handle

HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter
和HandlerFunctionAdapter三个接收的直接就是Requeset对象,不用SpringMVC做额外的解析,所以比较简单。

比较复杂的是RequestMappingHandlerAdapter,它执行的是加了@RequestMapping的方法,而这种方法的写法可以是多种多样,SpringMVC需要根据方法的定义去解析Request对象,从请求中获取出对应的数据然后传递给方法,并执行。

RequestMappingHandlerAdapter#handleInternal

    protected ModelAndView handleInternal(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        ModelAndView mav;
        // 检查当前请求的method是否为支持的method(默认Null,可通过继承AbstractController设置supportedMethods)
        // 检查当前请求是否必须session  (默认false,可通过继承AbstractController设置requireSession)
        checkRequest(request);

        /**
         * 判断当前是否需要支持在同一个session中只能线性地处理请求
         * 因为锁是通过 synchronized 是 JVM 进程级,所以在分布式环境下,
         * 无法达到同步相同 Session 的功能。默认情况下,synchronizeOnSession 为 false
         */
        if (this.synchronizeOnSession) {
            // 获取当前请求的session对象
            HttpSession session = request.getSession(false);
            if (session != null) {
                // 为当前session生成一个唯一的可以用于锁定的key
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized (mutex) {
                    // 对HandlerMethod进行参数等的适配处理,并调用目标handler
                    mav = invokeHandlerMethod(request, response, handlerMethod);
                }
            }
            else {
                // 如果当前不存在session,则直接对HandlerMethod进行适配
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // *如果当前不需要对session进行同步处理,则直接对HandlerMethod进行适配
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }


        //判断当前请求头中是否包含Cache-Control请求头,如果不包含,则对当前response进行处理
        if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
            // 如果当前SessionAttribute中存在配置的attributes,则为其设置过期时间。
            // 这里SessionAttribute主要是通过@SessionAttribute注解生成的
            if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            }
            else {
                // 如果当前不存在SessionAttributes,则判断当前是否存在Cache-Control设置,
                // 如果存在,则按照该设置进行response处理,如果不存在,则设置response中的
                // Cache的过期时间为-1,即立即失效
                prepareResponse(response);
            }
        }

        return mav;
    }

RequestMappingHandlerAdapter#invokeHandlerMethod

    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        // 把我们的请求req resp包装成 ServletWebRequest
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            // 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中
            // 配置的InitBinder,用于进行参数的绑定
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);

            // 获取容器中全局配置的ModelAttribute和当前HandlerMethod所对应的Controller 中配置的ModelAttribute,
            // 这些配置的方法将会在目标方法调用之前进行调用
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

            // 封装handlerMethod,会在调用前解析参数、调用后对返回值进行处理
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
            if (this.argumentResolvers != null) {
                // 让invocableMethod拥有参数解析能力
                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }
            if (this.returnValueHandlers != null) {
                // 让invocableMethod拥有返回值处理能力
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }
            // 让invocableMethod拥有InitBinder解析能力
            invocableMethod.setDataBinderFactory(binderFactory);
            // 设置ParameterNameDiscoverer,该对象将按照一定的规则获取当前参数的名称
            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
            // ModelAndView处理容器
            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            // 将request的Attribute复制一份到ModelMap
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            // *调用我们标注了@ModelAttribute的方法,主要是为我们的目标方法预加载
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);
            // 重定向的时候,忽略model中的数据 默认false
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

            // 获取当前的AsyncWebRequest,这里AsyncWebRequest的主要作用是用于判断目标
            // handler的返回值是否为WebAsyncTask或DeferredResult,如果是这两种中的一种,
            // 则说明当前请求的处理应该是异步的。所谓的异步,指的是当前请求会将Controller中
            // 封装的业务逻辑放到一个线程池中进行调用,待该调用有返回结果之后再返回到response中。
            // 这种处理的优点在于用于请求分发的线程能够解放出来,从而处理更多的请求,提高吞吐。
            // 只有待目标任务完成之后才会回来将该异步任务的结果返回。
            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);
            // 封装异步任务的线程池、request、interceptors到WebAsyncManager中
            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);
            }
            // *对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }

            // 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
            // 还会判断是否需要将FlashAttributes封装到新的请求中
            return getModelAndView(mavContainer, modelFactory, webRequest);
        }
        finally {
            webRequest.requestCompleted();
        }
    }

核心处理在ServletInvocableHandlerMethod#invokeAndHandle

    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {

        /*真正的调用我们的目标对象 很重要 很重要*/
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        // 设置相关的返回状态
        setResponseStatus(webRequest);
        // 如果请求处理完成,则设置requestHandled属性
        if (returnValue == null) {
            if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
                disableContentCachingIfNecessary(webRequest);
                mavContainer.setRequestHandled(true);
                return;
            }
        }
        // 如果请求失败,但是有错误原因,那么也会设置requestHandled属性
        else if (StringUtils.hasText(getResponseStatusReason())) {
            mavContainer.setRequestHandled(true);
            return;
        }

        mavContainer.setRequestHandled(false);
        Assert.state(this.returnValueHandlers != null, "No return value handlers");
        try {
            // 遍历当前容器中所有ReturnValueHandler,判断哪种handler支持当前返回值的处理,
            // 如果支持,则使用该handler处理该返回值
            this.returnValueHandlers.handleReturnValue(
                    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        }
        catch (Exception ex) {
            if (logger.isTraceEnabled()) {
                logger.trace(formatErrorForReturnValue(returnValue), ex);
            }
            throw ex;
        }
    }

InvocableHandlerMethod#invokeForRequest

    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);
    }

3.3.1 @RequestMapping方法参数解析

    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];
            //为我们得MethodParameter设置参数名称探测器对象
            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            args[i] = findProvidedArgument(parameter, providedArgs); // providedArgs
            if (args[i] != null) {
                continue;
            }
            // * 获取所有的参数解析器,然后筛选出合适的解析器
            if (!this.resolvers.supportsParameter(parameter)) {
                throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
            }
            try {
                // 通过上面筛选的 参数解析器来解析我们的参数
                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;
    }

一个HttpServletRequest通常有:

解析方法参通过HandlerMethodArgumentResolver来实现的,比如:

在判断某个参数该由哪个HandlerMethodArgumentResolver处理时,是很粗暴,HandlerMethodArgumentResolverComposite#getArgumentResolver:

    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
        HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
        if (result == null) {
            for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
                if (resolver.supportsParameter(parameter)) {
                    result = resolver;
                    this.argumentResolverCache.put(parameter, result);
                    break;
                }
            }
        }
        return result;
    }

就是遍历所有的HandlerMethodArgumentResolver,哪个能支持处理当前这个参数就由哪个处理。

类型转换

从请求中获取的值可能很多时候都是字符串,那如果参数类型不是String,该怎么办呢?这就需要进行类型转换了,比如代码是这么写的:

@RequestMapping(method = RequestMethod.GET, path = "/test")
@ResponseBody
public String test(@RequestParam User user) {
    System.out.println(user.getName());
return "hello zhouyu";
}

请求:http://localhost:8080/tuling-web/app/test?user=wz

那么SpringMVC就需要将字符串zhouyu转换成为User对象,这就需要我们自定义类型转换器了。

public class StringToUserEditor extends PropertyEditorSupport {

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        User user = new User();
        user.setName(text);
        this.setValue(user);
    }
}


@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(User.class, new StringToUserEditor());
}

Spring默认提供的Converter:org.springframework.core.convert.support.DefaultConversionService#addCollectionConverters

MultipartFile解析

当接收到一个请求后:

3.3.2 @RequestMapping方法返回值解析

方法返回值,也会分为不同的情况。比如有没有加@ResponseBody注解,如果方法返回一个String:

在SpringMVC中,会利用HandlerMethodReturnValueHandler来处理返回值:

这里着重分析RequestResponseBodyMethodProcessor,因为它会处理加了@ResponseBody注解的情况,也是目前我们用得最多的情况。

类型转换

RequestResponseBodyMethodProcessor相当于会把方法返回的对象直接响应给浏览器,如果返回的是一个字符串,那么好说,直接把字符串响应给浏览器,那如果返回的是一个Map呢?是一个User对象呢?该怎么把这些复杂对象响应给浏览器呢?

方法返回的是User对象,那么怎么把这个User对象返回给浏览器来展示呢?那得看当前请求设置的Accept请求头,比如我用Chrome浏览器发送请求,默认给我设置的就是:Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9

表示当前这个请求接收的内容格式,比如html格式、xml格式、各种图片格式等等。

如果我们的方法返回的是一个字符串,那么就对应html格式,就没问题,而如果我们不是返回的字符串,那我们就转成字符串,通常就是JSON格式的字符串。

处理这块,SpringMVC会利用HttpMessageConverter来处理,比如默认情况下,SpringMVC会有4个HttpMessageConverter:

StringHttpMessageConverter#writeInternal

    protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
        HttpHeaders headers = outputMessage.getHeaders();
        if (this.writeAcceptCharset && headers.get(HttpHeaders.ACCEPT_CHARSET) == null) {
            headers.setAcceptCharset(getAcceptedCharsets());
        }
        Charset charset = getContentTypeCharset(headers.getContentType());
        StreamUtils.copy(str, charset, outputMessage.getBody());
    }

先看有没有设置Content-Type,如果没有设置则取默认的,默认为ISO-8859-1,所以默认情况下返回中文会乱码。需要指定为UTF-8才能解决乱码问题。

以上四个Converter是不能处理Map对象或User对象的,所以如果返回的是Map或User对象,那么得单独配置一个Converter,比如MappingJackson2HttpMessageConverter,这个Converter比较强大,能把String、Map、User对象等等都能转化成JSON格式。

@Configuration
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        messageConverter.setDefaultCharset(StandardCharsets.UTF_8);
        converters.add(messageConverter);
    }
}
上一篇 下一篇

猜你喜欢

热点阅读