美文网首页Spring源码我爱编程
[SpringMVC]初始化源码分析

[SpringMVC]初始化源码分析

作者: 程序员驿站 | 来源:发表于2018-04-09 10:33 被阅读72次
    SpringMVCFlowDiagram.jpg

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

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

    屏幕快照 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.
         * <p>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.
         * <p>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.
         * <p>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<String, Object> attributesSnapshot = null;
            if (WebUtils.isIncludeRequest(request)) {
                attributesSnapshot = new HashMap<String, Object>();
                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]初始化源码分析

        本文链接:https://www.haomeiwen.com/subject/iabbhftx.html