spring之我见--Controller注册到DispatchServlet请求处理(下)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lovejj1994/article/details/79183829

1 DispatcherServlet请求分发

1.1 DispatcherServlet的初始化

在web.xml文件里,跟ContextLoaderListener形影不离的应该就是DispatcherServlet了,它一般做如下定义:

<servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>

DispatcherServlet是为SpringMVC服务的,在上一节中,我们知道了Controller如何被注册,所有的Mapping信息被保存在MappingRegistry里, DispatcherServlet接收到前台发来的请求便会 根据 MappingRegistry做匹配,最后用反射做到请求任务分发。

我们先看看DispatcherServlet是怎么初始化的,在DispatcherServlet的父类HttpServletBean中,每当IOC容器初始成功,会先调用init(),里面有个重要的方法,
initServletBean(),会对DispatcherServlet做一些参数的初始化。

注意:

<load-on-startup>1</load-on-startup>

一般来说,servlet的load-on-startup不设置的话,第一次请求才会触发init()方法,大于0的话就tomcat启动的时候就会触发。

一直我以为ApplicationContext在spring中只有一个,让我惊讶的是,这次servlet初始化也创建了一个ApplicationContext,而且以IOC容器的ApplicationContext为父类。

/**
为这个servlet创建了一个WebApplicationContext 
     * Instantiate the WebApplicationContext for this servlet, either a default
     * {@link org.springframework.web.context.support.XmlWebApplicationContext}
     * or a {@link #setContextClass custom context class}, if set.
     * <p>This implementation expects custom contexts to implement the
     * {@link org.springframework.web.context.ConfigurableWebApplicationContext}
     * interface. Can be overridden in subclasses.
     * <p>Do not forget to register this servlet instance as application listener on the
     * created context (for triggering its {@link #onRefresh callback}, and to call
     * {@link org.springframework.context.ConfigurableApplicationContext#refresh()}
     * before returning the context instance.
     * @param parent the parent ApplicationContext to use, or {@code null} if none
     * @return the WebApplicationContext for this servlet
     * @see org.springframework.web.context.support.XmlWebApplicationContext
     */
    protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
        Class<?> contextClass = getContextClass();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet with name '" + getServletName() +
                    "' will try to create custom WebApplicationContext context of class '" +
                    contextClass.getName() + "'" + ", using parent context [" + parent + "]");
        }
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException(
                    "Fatal initialization error in servlet with name '" + getServletName() +
                    "': custom WebApplicationContext class [" + contextClass.getName() +
                    "] is not of type ConfigurableWebApplicationContext");
        }
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

        wac.setEnvironment(getEnvironment());
        //将初始化IOC容器时的ApplicationContext作为父容器
        wac.setParent(parent);
        wac.setConfigLocation(getContextConfigLocation());

        configureAndRefreshWebApplicationContext(wac);

        return wac;
    }
protected final void initServletBean() throws ServletException {
        getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
        if (this.logger.isInfoEnabled()) {
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
        }
        long startTime = System.currentTimeMillis();

        try {
        //继续进这个方法
            this.webApplicationContext = initWebApplicationContext();

            initFrameworkServlet();
        }
        catch (ServletException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }
        catch (RuntimeException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }

        if (this.logger.isInfoEnabled()) {
            long elapsedTime = System.currentTimeMillis() - startTime;
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                    elapsedTime + " ms");
        }
    }

然后跟进configureAndRefreshWebApplicationContext(),一个比较重要的就是添加了一个Listener,这个监听会在ApplicationContext初始化完成后执行,Spring预留这个ApplicationListener就是给开发者提供方便做一些初始化操作。这个Listener做了什么后面再说,最后还是跟所有ApplicationContext初始化一样,走了refresh进行初始化。

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            // The application context id is still set to its original default value
            // -> assign a more useful id based on available information
            if (this.contextId != null) {
                wac.setId(this.contextId);
            }
            else {
                // Generate default id...
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                        ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
            }
        }

        wac.setServletContext(getServletContext());
        wac.setServletConfig(getServletConfig());
        //后面取dispatcher-servlet.xml文件路径有用
        wac.setNamespace(getNamespace());
        //添加一个listener,用于初始化
        wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

        // The wac environment's #initPropertySources will be called in any case when the context
        // is refreshed; do it eagerly here to ensure servlet property sources are in place for
        // use in any post-processing or initialization that occurs below prior to #refresh
        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
        }

        postProcessWebApplicationContext(wac);
        applyInitializers(wac);
        wac.refresh();
    }

refresh方法里比较重要的就是registerListeners() 和 finishRefresh() 里的publishEvent(new ContextRefreshedEvent(this)),前者将刚刚增加的Listener加入ApplicationEventMulticaster实例,Spring提供了ApplicationEventMulticaster接口,负责管理ApplicationListener和发布ApplicationEvent。后者从ApplicationEventMulticaster实例取出监听,执行监听的方法。下面是我们之前加入的那个监听。

    /**
     * ApplicationListener endpoint that receives events from this servlet's WebApplicationContext
     * only, delegating to {@code onApplicationEvent} on the FrameworkServlet instance.
     */
    private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {

        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            FrameworkServlet.this.onApplicationEvent(event);
        }
    }

注册监听到ApplicationEventMulticaster实例

/**
     * Add beans that implement ApplicationListener as listeners.
     * Doesn't affect other listeners, which can be added without being beans.
     */
    protected void registerListeners() {
        // Register statically specified listeners first.
        for (ApplicationListener<?> listener : getApplicationListeners()) {
            getApplicationEventMulticaster().addApplicationListener(listener);
        }

        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let post-processors apply to them!
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        for (String listenerBeanName : listenerBeanNames) {
            getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }

        // Publish early application events now that we finally have a multicaster...
        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (earlyEventsToProcess != null) {
            for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }

执行监听

@Override
    public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        invokeListener(listener, event);
                    }
                });
            }
            else {
                invokeListener(listener, event);
            }
        }
    }

这个监听的主要进行Servlet初始化操作,其中initHandlerMappings(context) 就是将父容器的RequestMappingHandlerMapping提取出来,因为所有的Mapping信息都存在里面,然后转成 handlerMappings供DispatchServlet分发请求。

/**
     * Initialize the strategy objects that this servlet uses.
     * <p>May be overridden in subclasses in order to initialize further strategy objects.
     */
    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }

具体的转换过程

/**
     * Initialize the HandlerMappings used by this class.
     * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
     * we default to BeanNameUrlHandlerMapping.
     */
    private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;

        if (this.detectAllHandlerMappings) {
            // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
            Map<String, HandlerMapping> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
                // We keep HandlerMappings in sorted order.
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        }
        else {
            try {
                HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                this.handlerMappings = Collections.singletonList(hm);
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default HandlerMapping later.
            }
        }

        // Ensure we have at least one HandlerMapping, by registering
        // a default HandlerMapping if no other mappings are found.
        if (this.handlerMappings == null) {
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
            if (logger.isDebugEnabled()) {
                logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
            }
        }
    }

至此,DispatchServlet的初始化告一段落,接下来就是具体的使用流程了。

1.2 DispatcherServlet的请求分发

还是老规矩看源码:

最先打交道的还是FrameworkServlet,是spring最基础的servlet,继承HttpServlet。

请求先从FrameworkServlet的service开始。

@Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
//因为父类不能处理PATCH请求,所以就直接processRequest处理
            processRequest(request, response);
        }
        else {
        //调用父类方法
            super.service(request, response);
        }
    }

请求链稍微有点兜兜转转, 然后根据 get,还是 post 等等又调用FrameworkServlet的do**方法,最后还是进入processRequest方法。
processRequest方法还没折腾完,FrameworkServlet会调用DispatcherServlet的doService方法,这时才开始进入正题。

DispatcherServlet的任务就是分发请求。在经过doService()的一些前置工作后,会委托给doDispatch()

doDispatch大概流程就是 根据 request 拿到请求url, 然后从mappingRegistry 取出匹配的HandlerMethod, HandlerMethod通过DispatcherServlet的getHandler方法可以获得一个HandlerExecutionChain对象实例,该实例封装了一个handler处理对象和一些interceptors.

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

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

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

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

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

HandlerMethod 转成 HandlerExecutionChain 逻辑

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

再往后还需要HandlerAdapter 的帮助,HandlerAdapter 根据HandlerExecutionChain 的映射信息 最后通过反射调用controller方法,完成请求处理。

// 根据HandlerMethod取到HandlerAdapter 
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 真正调用Controller的地方
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

2 参考文献

https://www.cnblogs.com/chenjunjie12321/articles/6118426.html
http://blog.csdn.net/wo541075754/article/details/71720984

猜你喜欢

转载自blog.csdn.net/lovejj1994/article/details/79183829