Spring源码学习【八】SpringMVC之DispatcherServlet

目录

一、前言

二、源码学习

(一)初始化阶段 -> init()

(二)响应客户请求 -> service()

(二)销毁阶段 -> destroy()

三、总结


一、前言

Web环境是Spring框架的重要应用场景,而SpringMVC又是Web开发中一个常用的框架,因此我们有必要学习一下SpringMVC的实现原理。

回到Web项目的配置文件web.xml中,在使用SpringMVC时我们需要进行如下的配置:

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/springMVC.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

熟悉Spring的同学对以上的配置肯定不陌生,这里配置了一个DispatcherServlet,这个Servlet是由Spring实现的,是SpringMVC最核心的部分,如上配置的这个Servlet会接收所有的请求,最终将请求分发至对应的Controller进行处理,下面我们就从DsipatcherServlet入手,学习SpringMVC的实现。

二、源码学习

首先,来看一看DsipatcherServlet的类继承关系(省略了部分接口):

 

从上图中可以看到,DispatcherServlet间接继承了HttpServlet,可用于处理Http请求。

既然DispatcherServlet也是Servlet家族中的一员,那么它肯定要遵循Servlet的生命周期,即:

  • 初始化阶段,调用init()方法 
  • 响应客户请求阶段,调用service()方法 
  • 销毁阶段,调用destroy()方法

有了这些了解,我们就可以顺着DispatcherServlet的生命周期来学习SpringMVC的实现了。

(一)初始化阶段 -> init()

首先,定位到初始化阶段,在这个阶段会调用init()方法,这个方法定义在Serlvet接口中,我们可以发现这个方法的最终实现在DispatcherServlet的父类HttpServletBean中,这个方法被定义为final方法,不可被子类覆盖,代码如下:

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {

    @Override
    public final void init() throws ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing servlet '" + getServletName() + "'");
        }
        // 获取配置的 init parameters,设置Bean的属性
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
                initBeanWrapper(bw);
                bw.setPropertyValues(pvs, true);
            }
            catch (BeansException ex) {
                if (logger.isErrorEnabled()) {
                    logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
                }
                throw ex;
            }
        }
        // 这个方法是一个模板方法,默认实现为空,交由其子类FrameworkServlet实现
        initServletBean();
        if (logger.isDebugEnabled()) {
            logger.debug("Servlet '" + getServletName() + "' configured successfully");
        }
    }

}

在上面的代码中,首先获取了init parameters,也就是web.xml中的<init-param/>节点,并将init parameters设置为这个Servlet Bean的属性,然后调用了子类FrameworkServlet的initServletBean()方法,进行额外的初始化处理,FrameworkServlet代码如下:

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {

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

    protected WebApplicationContext initWebApplicationContext() {
        // 首先从ServletContext中取得根应用上下文,也就是上一篇中在ContextLoader中创建的IOC容器
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;
        if (this.webApplicationContext != null) {
            // 若在构造Servlet时已经注入应用上下文,则直接使用
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {
                    if (cwac.getParent() == null) {
                        // 设置根应用上下文
                        cwac.setParent(rootContext);
                    }
                    // 配置并刷新应用上下文
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {
            // 构造Servlet时未注入应用上下文,则到ServletContext中获取
            wac = findWebApplicationContext();
        }
        if (wac == null) {
            // ServletContext中未获取到,则创建一个应用上下文
            // 这里创建应用上下文的处理与上一篇中的处理类似,不同之处在于创建完成后即进行了应用上下文的配置和刷新
            wac = createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
            // 触发初始化刷新,这里指的不是应用上下文的刷新
            // 这个方法是一个模板方法,默认实现为空,交由子类DisispatcherServlet实现
            onRefresh(wac);
        }

        if (this.publishContext) {
            // 将当前的应用上下文发布到ServletContext中,key为:FrameworkServlet.class.getName() + ".CONTEXT." + servletName
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]");
            }
        }
        return wac;
    }

}

上面的代码中,取得了一个应用上下文,作为了根IOC容器的子容器,这样,DispatcherServlet中的IOC容器就建立起来了,细心的同学会发现,在返回应用上下文之前调用了onRefresh(wac)方法,这个方法由其子类DispatcherServlet实现,用于初始化Web层需要的策略,下面让我们一起来看一看这部分的源码:

public class DispatcherServlet extends FrameworkServlet {

    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }

}

从上面的代码中可以看到,在获取应用上下文的过程中初始化了DispatcherServlet中需要的各种解析器,其中包括文件解析器、区域解析器、主题解析器等。

解析器的初始化过程大体相同,都是从应用上下文中取得相应的Bean,若不存在则使用默认解析器策略。

具体关于各解析器的介绍大家可以参考一篇博客:SpringMVC解析器

到这里,DispatcherServlet的初始化阶段就完成了,在这个过程中,一方面创建了DispatcherServlet的IOC容器,并将这个IOC容器作为根IOC容器的子容器,另一方面,初始化了DispatcherServlet需要的各种解析策略,接下来,DispatcherServlet将会在处理HTTP请求时发挥重要的作用。

(二)响应客户请求 -> service()

我们知道Servlet在接收到客户请求后会调用service()方法,根据请求类型执行doGet、doPost等一系列方法,在DispatcherServlet的继承体系中,由DispatcherServlet的父类FrameworkServlet重写了HttpServlet中的service()方法以及doGet()、doPost() 等一系列方法,下面以常用的HTTP请求方法来看一看FrameworkServlet的主要实现代码:

  • public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
    
        @Override
        protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
            // 这里添加了对patch请求的支持
            if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
                processRequest(request, response);
            }
            else {
                // 这里调用HttpServlet的service方法,根据请求类型调用该类中重写的doGet、doPost等方法 
                super.service(request, response);
            }
        }
    
        @Override
        protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            processRequest(request, response);
        }
    
        @Override
        protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            processRequest(request, response);
        }
    
        @Override
        protected final void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            processRequest(request, response);
        }
    
        @Override
        protected final void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            processRequest(request, response);
        }
    
    }

从上面的代码中可以看到, DispatcherServlet接收到用户请求后,会调用父类FrameworkServlet中的service()方法,最终根据请求类型调用FrameworkServlet中重写的doGet、doPost等方法,这些方法都调用了processRequest()方法,下面让我们看一下processRequest()的具体实现:

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {

    protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        // 获取LocaleContext(语言环境), 用作备份
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        // 根据当前request创建LocaleContext
        LocaleContext localeContext = buildLocaleContext(request);
        // 获取RequestAttributes,用作备份
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        // 根据当前request、response创建ServletRequestAttributes
        ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
        // 为当前请求注册一个拦截器,用于在请求执行前后异步初始化和重置FrameworkServlet的LocaleContextHolder和RequestContextHolder
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
        // 根据当前request初始化ContextHolders
        initContextHolders(request, localeContext, requestAttributes);
        try {
            // 具体处理请求,是一个模板方法,由子类DispatcherServlet实现
            doService(request, response);
        }
        catch (ServletException | IOException ex) {
            failureCause = ex;
            throw ex;
        }
        catch (Throwable ex) {
            failureCause = ex;
            throw new NestedServletException("Request processing failed", ex);
        }
        finally {
            // 使用备份重置ContextHolders
            resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }
            if (logger.isDebugEnabled()) {
                if (failureCause != null) {
                    this.logger.debug("Could not complete request", failureCause);
                }
                else {
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        logger.debug("Leaving response open for concurrent processing");
                    }
                    else {
                        this.logger.debug("Successfully completed request");
                    }
                }
            }
            // 向该应用注册的所有监听器发布RequestHandledEvent事件
            // 监听器可以通过实现ApplicationListener接口来实现
            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }

}

在上面的代码中我们可以看到一个用于处理请求的核心方法:doService(request, response),这个方法是一个模板方法,由其子类DispatcherServlet实现,代码如下:

public class DispatcherServlet extends FrameworkServlet {

    @Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (logger.isDebugEnabled()) {
            String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
            logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
        }

        // 对于include请求,首先保存request属性快照,用于请求后恢复属性
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap<>();
            Enumeration<?> attrNames = request.getAttributeNames();
            while (attrNames.hasMoreElements()) {
                String attrName = (String) attrNames.nextElement();
                if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
        }

        // 为request设置一些必要的属性
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); // 应用上下文
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); // 语言环境解析器
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); // 主题解析器
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); // 主题源,默认将应用上下文作为主题源

        if (this.flashMapManager != null) {
            FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
            if (inputFlashMap != null) {
                request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
            }
            request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
            request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
        }

        try {
            // 分发请求
            doDispatch(request, response);
        }
        finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                // Restore the original attribute snapshot, in case of an include.
                if (attributesSnapshot != null) {
                    // 恢复include请求属性
                    restoreAttributesAfterInclude(request, attributesSnapshot);
                }
            }
        }
    }

}

doService方法比较简单,主要是为request设置一些必要的属性,接下来调用了doDispatch方法进行请求的分发,这是SpringMVC中的核心功能,doDispatch方法中主要进行了如下的处理:

  • 处理拦截
  • 处理请求
  • 解析View

代码如下:

public class DispatcherServlet extends FrameworkServlet {

    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 {
                // 判断是否为文件上传请求,这里会尝试将request转换为文件上传请求
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // 通过HandlerMapping获取请求匹配的处理器:一个HandlerExecutionChain类的实例
                // 这个处理器中包含一个请求处理器和多个拦截器
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    // 未找到匹配的处理器,返回404错误
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 根据匹配的请求处理器获取支持该处理器的适配器
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                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);
                    }
                    // 对于get请求,如果从上次修改后未进行修改则不再对请求进行处理
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                // 执行拦截器 -> preHandle
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // 执行处理器处理请求,返回ModelAndView对象
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                // 若ModelAndView无视图名,则为其设置默认视图名
                applyDefaultViewName(processedRequest, mv);
                // 执行拦截器 -> postHandle
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            // 处理异常,解析View
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            // 触发拦截器的afterCompletion方法
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            // 触发拦截器的afterCompletion方法
            triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err));
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // 清除文件上传请求使用的资源
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

}

到这里,客户的请求就相应完成了。在这个过程中,首先处理匹配的处理器中的拦截器,然后通过处理器处理客户的请求,最后通过视图解析器解析和渲染视图,这里还有许多细节未深入分析,我们将在后续继续学习。

(二)销毁阶段 -> destroy()

Servlet的销毁阶段会调用destroy()方法,这个方法的实现在FrameworkServlet中,实现比较简单,就是将Servlet中的IOC容器关闭,代码如下:

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {

    @Override
    public void destroy() {
        getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");
        // Only call close() on WebApplicationContext if locally managed...
        if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) {
            ((ConfigurableApplicationContext) this.webApplicationContext).close();
        }
    }
}

三、总结

本篇中,顺着Servlet的生命周期大致分析了SpringMVC的核心类DispatcherServlet的实现,对SpringMVC的请求控制有了一定的了解,但在DispatcherServlet处理客户请求的部分有许多内容未深入分析,需要进一步学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值