zoukankan      html  css  js  c++  java
  • Web容器初始化过程

    一、SpringMVC启动过程

    Spring的MVC是基于Servlet功能实现的,每个web工程中都有一个web.xml文件,web容器在启动的时候会加载这个配置文件,当一个web应用加载到web容器中后,在web应用开始响应客户端的请求之前,要按照顺序执行下面几个步骤:

    1、实例化部署描述符中的<listener>元素标识的时间监听器的实例;

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    2、在实现了ServletContextListener接口的监听器实例中,调用contextInitialized()方法。ContextLoaderListener实现了ServletContextListener接口,ServletContextListener作为Servlet的监听者,会在ServletContext创建、销毁过程中监听ServletContextEvent事件,然后进行响应的处理。换句话说,就是在启动web容器时,ContextLoaderListener类会自动装配ApplicationContext的配置信息。

    public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
        public ContextLoaderListener() {
        }
    
        public ContextLoaderListener(WebApplicationContext context) {
            super(context);
        }
      // 这里调用其父类ContextLoader的initWebApplicationContext进行初始化工作   
        public void contextInitialized(ServletContextEvent event) {
            this.initWebApplicationContext(event.getServletContext());
        }
      
        public void contextDestroyed(ServletContextEvent event) {
            this.closeWebApplicationContext(event.getServletContext());
            ContextCleanupListener.cleanupAttributes(event.getServletContext());
        }
    }

      ContextLoaderListener类继承了ContextLoader,后者是实际的ServletContextListener接口实现者,这里用到了代理模式,利用ContextLoader类创建Spring application context,并将其注册到ServletContext中去。ContextLoaderListener就是调用ContextLoader类的initWebApplicationContext()方法和closeWebApplicationContext()方法来实现ServletContext的创建和销毁工作的。

      

       其中,initWebApplicationContext()方法主要完成了两个部分的工作:一是查看是否已经指定父容器,如果不存在则设置其父容器。二是将Spring WebApplicationContext放置到线程Map中。

    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
            if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
                throw new IllegalStateException("Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!");
            } else {
                Log logger = LogFactory.getLog(ContextLoader.class);
                servletContext.log("Initializing Spring root WebApplicationContext");
                if (logger.isInfoEnabled()) {
                    logger.info("Root WebApplicationContext: initialization started");
                }
    
                long startTime = System.currentTimeMillis();
    
                try {
                    if (this.context == null) {
                        this.context = this.createWebApplicationContext(servletContext);
                    }
    
                    if (this.context instanceof ConfigurableWebApplicationContext) {
                        ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
                        if (!cwac.isActive()) {
                  //查看是否指定父容器
                            if (cwac.getParent() == null) {
                                ApplicationContext parent = this.loadParentContext(servletContext);
                                cwac.setParent(parent);
                            }
    
                            this.configureAndRefreshWebApplicationContext(cwac, servletContext);
                        }
                    }
    
                    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
                    ClassLoader ccl = Thread.currentThread().getContextClassLoader();
                    //将WebApplicationContext放置到线程Map中
                    if (ccl == ContextLoader.class.getClassLoader()) {
                        currentContext = this.context;
                    } else if (ccl != null) {
                        currentContextPerThread.put(ccl, this.context);
                    }
    
                    if (logger.isDebugEnabled()) {
                        logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
                    }
    
                    if (logger.isInfoEnabled()) {
                        long elapsedTime = System.currentTimeMillis() - startTime;
                        logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
                    }
    
                    return this.context;
                } catch (RuntimeException var8) {
                    logger.error("Context initialization failed", var8);
                    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);
                    throw var8;
                } catch (Error var9) {
                    logger.error("Context initialization failed", var9);
                    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9);
                    throw var9;
                }
            }
        }    

    在初始化方法执行后,将WebApplicationContext存到了两个地方,分别是ServletContext和currentContextPerThread中,我们可以通过这两个地方来获取WebApplicationContext:1.从servletContext中去拿;2.从当前的线程Map中去拿。

    3、实例化部署符中<filter>元素标识的过滤器实例,并调用每个过滤器实例的init()方法。

    4、实例化部署符中<servlet>元素标识的Servlet实例,按照该元素包含的load-on-startup顺序,调用每个Servlet实例的init()方法。

      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

    在SpringMVC的架构中,DispatcherServlet主要负责客户端请求的分发,起到控制器的作用。DispatcherServlet是实现Servlet接口的实现类,其结构如下图所示。Servlet的生命周期由Servlet的容器来控制,主要分为初始化、运行和销毁三个阶段,分别对应三个方法:init()、service()和destory()。

      在DispatcherServlet的父类HTTPServletBean中,重写了init()方法,主要实现将当前的Servlet类实例转换为BeanWrapper类型实例,以便使用Spring中提供的注入功能进行相应属性的注入。

      在DispatcherServlet的父类FameworkServelet中,重写了service()方法,对于不同的方法,Spring统一将程序引导至processRequest(request,response)中,processRequest方法再对处理请求进行了一些准备工作后,又将细节实现转移到了doService方法中。

      protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String method = request.getMethod();
            if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
                this.processRequest(request, response);
            } else {
                super.service(request, response);
            }
    
       }
      protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            long startTime = System.currentTimeMillis();
            Throwable failureCause = null;
            LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
            LocaleContext localeContext = this.buildLocaleContext(request);
            RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor());
            this.initContextHolders(request, localeContext, requestAttributes);
            try {
                //调用DispatcherServlet的doService()方法
                this.doService(request, response);
            } catch (ServletException var17) {
                failureCause = var17;
                throw var17;
            } catch (IOException var18) {
                failureCause = var18;
                throw var18;
            } catch (Throwable var19) {
                failureCause = var19;
                throw new NestedServletException("Request processing failed", var19);
            } finally {
                this.resetContextHolders(request, previousLocaleContext, previousAttributes);
                if (requestAttributes != null) {
                    requestAttributes.requestCompleted();
                }
                if (this.logger.isDebugEnabled()) {
                    if (failureCause != null) {
                        this.logger.debug("Could not complete request", (Throwable)failureCause);
                    } else if (asyncManager.isConcurrentHandlingStarted()) {
                        this.logger.debug("Leaving response open for concurrent processing");
                    } else {
                        this.logger.debug("Successfully completed request");
                    }
                }
                this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
            }
        }        
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
            if (this.logger.isDebugEnabled()) {
                String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
                this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
            }
    
            Map<String, Object> attributesSnapshot = null;
            if (WebUtils.isIncludeRequest(request)) {
                attributesSnapshot = new HashMap();
                Enumeration attrNames = request.getAttributeNames();
                label108:
                while(true) {
                    String attrName;
                    do {
                        if (!attrNames.hasMoreElements()) {
                            break label108;
                        }
    
                        attrName = (String)attrNames.nextElement();
                    } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
    
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
            request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
            request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
            request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
            request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.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 {
               // 最终的请求处理函数
                this.doDispatch(request, response);
            } finally {
                if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                    this.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 {
                try {
                    ModelAndView mv = null;
                    Exception dispatchException = null;
    
                    try {
                        processedRequest = this.checkMultipart(request);
                        multipartRequestParsed = processedRequest != request;
                    //根据request信息寻找对应的处理器
                        mappedHandler = this.getHandler(processedRequest);
                        if (mappedHandler == null || mappedHandler.getHandler() == null) {
                            this.noHandlerFound(processedRequest, response);
                            return;
                        }
                    //根据处理器寻找对应的处理器适配器
                        HandlerAdapter ha = this.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 (this.logger.isDebugEnabled()) {
                                this.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;
                        }
                       //返回视图
                        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                        if (asyncManager.isConcurrentHandlingStarted()) {
                            return;
                        }
    
                        this.applyDefaultViewName(request, mv);
                        mappedHandler.applyPostHandle(processedRequest, response, mv);
                    } catch (Exception var19) {
                        dispatchException = var19;
                    }
    
                    this.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
                } catch (Exception var20) {
                    this.triggerAfterCompletion(processedRequest, response, mappedHandler, var20);
                } catch (Error var21) {
                    this.triggerAfterCompletionWithError(processedRequest, response, mappedHandler, var21);
                }
    
            } finally {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    if (mappedHandler != null) {
                        mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                    }
                } else if (multipartRequestParsed) {
                    this.cleanupMultipart(processedRequest);
                }
    
            }
        }    

    二、ServletContext、ApplicationContext和WebApplicationContext区别?

      1、ServletContext:是一个Web应用的上下文,是一个全局信息的存储空间,代表当前的web应用。Servlet容器(如Tomcat)在启动一个Webapp时,会为它创建一个ServletContext对象,即servlet上下文环境。每个webapp都有唯一的ServletContext对象。同一个webapp的所有servlet对象共享一个ServeltContext,servlet对象可以通过ServletContext来访问容器中的各种资源。ServletContext在web应用启动时创建,在web应用服务关闭时释放。

       2、ApplicationContext:它是建立在BeanFactory基础之上的,除了包含BeanFactory的所有功能之外,在国际化支持、资源访问、事件传播等方面进行了良好的支持。其体系结构如下图。

      ApplicationContext接口有三个常用的实现类,如下:

    类名称 功能描述
    ClassPathXmlApplicationContext
    从类路径ClassPath中寻找指定的XML配置文件,找到并装载
    完成ApplicationContext的实例化工作
    FileSystemXmlApplicationContext
    从指定的文件系统路径中寻找指定的XML配置文件,找到并装载
    完成ApplicationContext的实例化工作
    XmlWebApplicationContext 从Web应用中寻找指定的XML配置文件,找到并装载完成ApplicationContext的实例化工作

      与BeanFactory不同的是,ApplicationContext容器实例化后会自动对所有的单实例Bean进行实例化与依赖关系的装配,使之处于待用状态。而BeanFactory容器实例化后并不会自动实例化Bean,只有当Bean被使用时BeanFactory容器才会对该Bean进行实例化与依赖关系的装配。

      3、WebApplicationContext:它是专门为web应用提供的,它允许从相对于web根目录路径中装载配置文件完成初始化;从WebApplicationContext中可以获得ServletContext的引用,同时为了方便web应用访问Spring应用上下文,WebApplicationContext也将作为一个属性放到ServletContext中,可以通过WebApplicationContextUtils的getWebApplicationContext(ServletContext sc)方法获取。

    三、总结

      ApplicationContext是Spring的核心,Context为上下文环境,在Web应用中,会用到WebApplicationContext,它继承自ApplicationContext。在SpringMVC启动过程中,ContextLoaderListener类起着至关重要的作用。它读取web.xml中配置的context-param中的配置文件,提前在web容器初始化前准备业务对应的Application context,将创建好的WebApplicationContext放置于ServletContext属性中,这样我们只要得到Servlet就可以得到WebApplicationContext对象,并利用这个对象访问spring容器管理的bean。

    四、参考资料

      1、https://www.cnblogs.com/RunForLove/p/5688731.html

      2、spring源码深度解析(郝佳)

  • 相关阅读:
    代码: 仿淘宝商品详情页左上,图片鼠标浮上去,图片部分区域放大 (页面布局:图片列表)
    !代码:页面布局
    代码: 返回页面顶部 jquery
    !!学习笔记:CSS3动画
    代码: 两列图片瀑布流(一次后台取数据,图片懒加载。下拉后分批显示图片。图片高度未知,当图片onload后才显示容器)
    !学习笔记:前端测试 、前端调试、console 等
    学习笔记:SASS
    !代码:伪类
    代码:css小图标
    学习笔记:ECharts
  • 原文地址:https://www.cnblogs.com/xujian2014/p/9649822.html
Copyright © 2011-2022 走看看