SpringBoot启动源码解读(15)

这次我们说的是Spring接收消息的过程:
1:Spring有一个处理http请求的类:DispatcherServlet。在Springboot启动过程中,在DispatcherServletAutoConfiguration 类里,会创建 DispatcherServlet 实例,代码如下:

@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        public DispatcherServlet dispatcherServlet() {
            DispatcherServlet dispatcherServlet = new DispatcherServlet();
            dispatcherServlet.setDispatchOptionsRequest(
                    this.webMvcProperties.isDispatchOptionsRequest());
            dispatcherServlet.setDispatchTraceRequest(
                    this.webMvcProperties.isDispatchTraceRequest());
            dispatcherServlet.setThrowExceptionIfNoHandlerFound(
                    this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
            return dispatcherServlet;
        }

2:在这个类中,也注册了 ServletRegistrationBean 实例,其构造函数中,有参数 DispatcherServlet。 如下:

@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
        @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        public ServletRegistrationBean dispatcherServletRegistration(
                DispatcherServlet dispatcherServlet) {
            ServletRegistrationBean registration = new ServletRegistrationBean(
                    dispatcherServlet, this.serverProperties.getServletMapping());
            registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
            registration.setLoadOnStartup(
                    this.webMvcProperties.getServlet().getLoadOnStartup());
            if (this.multipartConfig != null) {
                registration.setMultipartConfig(this.multipartConfig);
            }
            return registration;
        }

3:在上一篇的tomcat启动过程中,一个很重要的方法就是调用 ServletContextInitializer接口的 onStartup 方法。而 ServletRegistrationBean 类就实现了该接口。在该方法中,很重要的就是把 DispatcherServlet 实例加入ServletContext 中。 代码如下:

@Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        Assert.notNull(this.servlet, "Servlet must not be null");
        String name = getServletName();
        if (!isEnabled()) {
            logger.info("Servlet " + name + " was not registered (disabled)");
            return;
        }
        logger.info("Mapping servlet: '" + name + "' to " + this.urlMappings);
        Dynamic added = servletContext.addServlet(name, this.servlet);
        if (added == null) {
            logger.info("Servlet " + name + " was not registered "
                    + "(possibly already registered?)");
            return;
        }
        configure(added);
    }

4:在tomcat启动后,就可以接受http请求了。首先是 Connector 接受请求,其调用顺序如下是:Connector --> Engine --> Host --> Context -->Servlet。这里的Servlet 就是 DispatcherServlet 实例。Engine 调用 Host 的代码如下(其他类似):

public final void invoke(Request request, Response response)
        throws IOException, ServletException {
        // Select the Host to be used for this Request
        Host host = request.getHost();
        if (host == null) {
          ......
        }
        if (request.isAsyncSupported()) {
            request.setAsyncSupported(host.getPipeline().isAsyncSupported());
        }
        // Ask this Host to process this request
        host.getPipeline().getFirst().invoke(request, response);

    }

5:在 WebMvcAutoConfiguration 配置中,会配置 请求对象是配置:RequestMappingHandlerAdapter,代码如下:

@Bean
        @Override
        public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
            RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
            adapter.setIgnoreDefaultModelOnRedirect(this.mvcProperties != null
                    ? this.mvcProperties.isIgnoreDefaultModelOnRedirect() : true);
            return adapter;
        }

6:在 WebMvcAutoConfiguration 中会注册 RequestMappingHandlerMapping 实例。如下:

@Bean
        @Primary
        @Override
        public RequestMappingHandlerMapping requestMappingHandlerMapping() {
            // Must be @Primary for MvcUriComponentsBuilder to work
            return super.requestMappingHandlerMapping();
        }

7:在 RequestMappingHandlerMapping 类匹配的注解是:@Controller 和 @RequestMapping。

protected boolean isHandler(Class<?> beanType) {
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }

该类的 afterPropertiesSet 方法里,会解析类和方法的 @RequestMapping 注解,并将结果缓存到 MappingRegistry 中。

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        RequestMappingInfo info = createRequestMappingInfo(method);
        if (info != null) {
            RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
            if (typeInfo != null) {
                info = typeInfo.combine(info);
            }
        }
        return info;
    }
protected void initHandlerMethods() {
        String[] beanNames = getApplicationContext().getBeanNamesForType(Object.class));
        for (String beanName : beanNames) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                Class<?>    beanType = getApplicationContext().getType(beanName);
                if (beanType != null && isHandler(beanType)) {
                    detectHandlerMethods(beanName);
                }
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

8:RequestMappingHandlerAdapter 继承了 InitializingBean 接口。在初始化后,会调用 afterPropertiesSet 方法。在该方法中,会注册请求和相应参数的解析类:比如:
@RequestParam 的处理类:RequestParamMethodArgumentResolver
@PathVariable 的处理类:PathVariableMethodArgumentResolver
@RequestBody 的处理类:RequestResponseBodyMethodProcessor
@ResponseBody 的处理类:RequestResponseBodyMethodProcessor
代码如下:

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

8:在方法到达 DispatcherServlet 后,会调用 service 方法。如果该方法是Post请求,则调用 doPost 方法,如果是 GET 请求,则调用 doGet 方法。DELETE,PUT等请求方法类似。 代码如下:

      } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);

9:doPost 方法进入后,方法调用顺序是:doPost() --> processRequest() --> doService() --> doDispatch()。
在这个方法里,首先根据请求的URL找到对应的@RequestMapping注册信息,然后生成对应的 HandlerExecutionChain 对象。如果类型有@Controller或者@RequestMapping注解,则找到的handler是 RequestMappingHandlerAdapter 实例。代码如下:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = getHandlerInternal(request);
        if (handler == null) {
            handler = getDefaultHandler();
        }
        if (handler == null) {
            return null;
        }
        // Bean name or resolved handler?
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = getApplicationContext().getBean(handlerName);
        }

        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        if (CorsUtils.isCorsRequest(request)) {
            CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }
        return executionChain;
    }

10:拿到 Handler 后,调用的方法顺序是:handle() --> handleInternal() --> invokeHandlerMethod()。 在方法 invokeHandlerMethod 里,创建 ServletInvocableHandlerMethod 类型对象,然后调用 invokeAndHandle 方法。

public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {

        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
                    "' with arguments " + Arrays.toString(args));
        }
        Object returnValue = doInvoke(args);
        if (logger.isTraceEnabled()) {
            logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
                    "] returned [" + returnValue + "]");
        }
        return returnValue;
    }

11:在上面的方法里,首先会进行参数解析。这里的方法就是根据参数的各种纾解去找到对应的解析类。比如注解 @RequestBody 的处理类就是RequestResponseBodyMethodProcessor。 然后调用 resolveArgument 方法解析对象。

public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

        HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
        if (resolver == null) {
            throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
        }
        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }

12:参数解析后,利用反射,执行 invoke 方法。对返回的参数也要进行解析。代码如下(和请求参数比较类型):

public void handleReturnValue(Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

        HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
        if (handler == null) {
            throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
        }
        handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
    }
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。