[SpringMVC]初始化源码分析

[SpringMVC]初始化源码分析_第1张图片
SpringMVCFlowDiagram.jpg

图片地址来自:https://tutorialspointexamples.com/spring-mvc-framework-beginners-architecture-diagram-execution-flow/

大家都知道Springmvc的核心是DispatcherServlet,而实现Servlet,类图关系;

[SpringMVC]初始化源码分析_第2张图片
屏幕快照 2018-04-09 上午10.26.15.png

大家都知道Servlet的生命周期分为,init service destory;init和destroy只执行一次
那么DispatcherServlet会在这三个阶段都做了哪些事?

init 阶段

大概流程

HttpServletBean#init() --将配置参数映射到此servlet的bean属性上,调用子类初始化
-> 从初始化参数设置bean属性
-> 将当前servlet类转换一个BeanWrapper
-> 自动以属性编辑器
-> 检测配置的参数
-> initBeanWrapper(bw) 空是实现
-> FrameworkServlet#initServletBean
    -> initWebApplicationContext(); --初始化WebApplicationContext
        -> 先从servletContext上下文获取
        -> 如果拿到。。。。
        -> 没有获取到
        -> 从配置名称为ServletContext属性中检索webapplication上下文。
        -> createWebApplicationContext(); 创建webApplicationContext
            -> 读取XmlWebApplicationContext.class
            -> 初始化XmlWebApplicationContext
            -> 设置环境 如果没有则创建StandardServletEnvironment
            -> 设置父容器 WebApplicationContext
            -> 设置configLocation
            -> 配置并且重启WebApplicationContext
                -> 设置servlet上下文
                -> 设置配置信息
                -> 设置namespace
                -> 设置一个ContextRefreshListener
                -> 给子类留下一个hook postProcessWebApplicationContext
                -> applyInitializers 在webapplicationcontextinizerclass servlet init - param指定的任何应用程序上下文初始值设定项实例刷新之前,委托webapplicationcontextizerclass。
                -> ConfigurableWebApplicationContext 刷新
     -> initFrameworkServlet();

源码解析

1、先看下HttpServletBean#init()

/**
     * 将配置参数映射到servlet的bean属性上,并且调用子类初始化 模板方法
     * Map config parameters onto bean properties of this servlet, and
     * invoke subclass initialization.
     *
     * @throws ServletException if bean properties are invalid (or required
     *                          properties are missing), or if subclass initialization fails.
     */
    @Override
    public final void init() throws ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing servlet '" + getServletName() + "'");
        }

        // Set bean properties from init parameters
        // 解析配置的init-param并且封装到PropertyValues.
        try {
            PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
            // 将当前的servlet类转换为BeanWrapper。从而能够被Spring对init-param的值进行注入
            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) {
            logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            throw ex;
        }

        // Let subclasses do whatever initialization they like.
        // 留给子类实现,这里是被FrameworkServlet实现的
        initServletBean();

        if (logger.isDebugEnabled()) {
            logger.debug("Servlet '" + getServletName() + "' configured successfully");
        }
    }

2、接着看下 FrameworkServlet# initServletBean();

/**
     * 重写HttpServletBean的方法,在设置任何bean属性后调用。创建此servlet的WebApplicationContext
     * Overridden method of {@link HttpServletBean}, invoked after any bean properties
     * have been set. Creates this servlet's WebApplicationContext.
     */
    @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 {
            // 初始化WebApplicationContext
            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");
        }
    }

3、初始化WebApplicationContext

/**
     * 初始化并且发布servlet的WebApplicationContext的实例
     * Initialize and publish the WebApplicationContext for this servlet.
     * 

Delegates to {@link #createWebApplicationContext} for actual creation * of the context. Can be overridden in subclasses. * * @return the WebApplicationContext instance 返回WebApplicationContext实例 * @see #FrameworkServlet(WebApplicationContext) * @see #setContextClass * @see #setContextConfigLocation */ protected WebApplicationContext initWebApplicationContext() { // 对servlet功能所使用的变量进行初始化 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it // 在构造时注入了上下文实例,直接使用 wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } // 刷新上下文 configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one 在构造时未注入上下文实例 // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id // 根据contextAttribute属性加载WebApplicationContext wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one //没有为此servlet定义上下文实例 wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. onRefresh(wac); } if (this.publishContext) { // Publish the context as a servlet context attribute. 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; }

4、主要看下createWebApplicationContext(rootContext);

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
        // XmlWebApplicationContext.class 默认
        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 + "]");
        }
        // 必须实现ConfigurableWebApplicationContext接口
        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());
        wac.setParent(parent);
        wac.setConfigLocation(getContextConfigLocation());

        configureAndRefreshWebApplicationContext(wac);

        return wac;
    }

4、onRefresh(wac);

/**
     * This implementation calls {@link #initStrategies}.
     */
    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    /**
     * 初始化此servlet使用的策略对象。
     * 以便初始化进一步的策略对象
     * Initialize the strategy objects that this servlet uses.
     * 

May be overridden in subclasses in order to initialize further strategy objects. */ protected void initStrategies(ApplicationContext context) { // 初始化MultipartResolver 用来处理上传文件 默认是没有处理的,需要在上下文添加MultipartResolver处理器 // org.springframework.web.multipart.commons.CommonsMultipartResolver // org.springframework.web.multipart.support.ByteArrayMultipartFileEditor // org.springframework.web.multipart.support.StringMultipartFileEditor initMultipartResolver(context); // 初始化LocaleResolver 配置国际化的 initLocaleResolver(context); // 初始化ThemeResolver 通过主题控制页面风格 initThemeResolver(context); // 初始化HandlerMappings 由定义请求和处理程序对象之间映射的对象实现 initHandlerMappings(context); // 初始化HandlerAdapters initHandlerAdapters(context); // 初始化HandlerExceptionResolvers 异常处理 initHandlerExceptionResolvers(context); // 初始化RequestToViewNameTranslator initRequestToViewNameTranslator(context); // 初始化ViewResolvers initViewResolvers(context); // 初始化FlashMapManager initFlashMapManager(context); }

初始化阶段的工作基本做完了,各个方法细节东西,大家可以自己看下内部实现。每个方法的代码非常清晰。

逻辑处理阶段

调用栈

at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:743)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:672)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:82)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:919)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:851)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:953)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:844)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:829)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)

流程

我们发起http的get请求或者post请求,都是HttpServlet的service方法根据http协议的方法来区分的处理逻辑,见下面代码

protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } 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);
            
        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }

Spring的FrameworkServlet重写了doGet或者doPost方法

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

整理处理的实际上processRequest方法,我们看下processRequest这个方法都干了啥?看了下这个方法的注释

/**
     * 处理此请求,发布事件而不考虑结果。
     实际事件处理由抽象doService模板方法执行
     * Process this request, publishing an event regardless of the outcome.
     * 

The actual event handling is performed by the abstract * {@link #doService} template method. */ protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 初始化当前时间,用户记录web请求的时间 long startTime = System.currentTimeMillis(); Throwable failureCause = null; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = buildLocaleContext(request); // 上request的参数 放到ThreadLocal 里面供非controller层调用 RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); initContextHolders(request, localeContext, requestAttributes); try { // 重点 doService(request, response); } catch (ServletException ex) { failureCause = ex; throw ex; } catch (IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { 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"); } } } publishRequestHandledEvent(request, response, startTime, failureCause); } }

doService被DispatcherServlet重写了,重点来了

/**
     * 设置DispatcherServlet请求属性和委托给doDispatch以进行实际调度
     * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
     * for the actual dispatching.
     */
    @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) + "]");
        }

        // Keep a snapshot of the request attributes in case of an include,
        // to be able to restore the original attributes after the include.
        Map 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("org.springframework.web.servlet")) {
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
        }

        // Make framework objects available to handlers and view objects.
        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());

        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) {
                    restoreAttributesAfterInclude(request, attributesSnapshot);
                }
            }
        }
    }

注释是说明实际调度是doDispatch,哦这才是重点,迫不及待的看看他都实现了啥

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

                // 根据request查找对应的handler
                mappedHandler = getHandler(processedRequest);
                // 如果没有handler处理直接返回错误信息
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 确定当前请求的处理程序适配器.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // 处理last-modified (如果处理程序支持)
                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;
                    }
                }
                // 已注册拦截器的预处理方法 如果返回为true 继续执行,返回false 该请求直接返回
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // 实际调用处理程序.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

                applyDefaultViewName(request, mv);
                // 注册拦截器的事后处理方法
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } catch (Exception ex) {
                dispatchException = ex;
            }
            // 处理程序调用的结果
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        } catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        } catch (Error err) {
            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);
                }
            }
        }
    }

里面细节的方法 后续单独分析

destroy

这个阶段干的事情比较少,就是关闭一些东西

/**
     * Close the WebApplicationContext of this servlet.
     *
     * @see org.springframework.context.ConfigurableApplicationContext#close()
     */
    @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();
        }
    }

总结

1、

你可能感兴趣的:([SpringMVC]初始化源码分析)