The relationship between the various context of java web

I lifted this article solve a lot of my doubts, I had to sort out the relationship is not clear Context, read this article very helpful in understanding the source,

Original link here: https://www.jianshu.com/p/2537e2fec546

I reproduced it in his blog, the fear of the future can not be found, reads as follows

 

Online blog to see a word, it is to describe and depict the relationship between web application context, to quote it here to illustrate: If under "context" do not know, I am here to say, the program inside the so-called "context" is program execution environment, an analogy: your home, right? If you do not have a home study program, the rent is also OK ah! You the equivalent of a web application, web application context is equivalent to home, you can put something at home, you can take things, your basic needs are dependent on the family, this home is your life contexts.

The blog address: the Spring and SpringMVC configuration relationship of father and son WebApplicationContext

Spring start-up process

first step:

 First, for a web application, which is deployed in the container web, which web container to provide a global context, the context is the ServletContext which provides the host environment for the following spring IoC container;

Step two:

 Secondly, there will be provided in web.xml contextLoaderListener. When starting the web container, the container will trigger initialization event, this time contextLoaderListener will listen to this event, which contextInitialized method is called, in this method, spring will start to initialize a context which is known as the root context, namely WebApplicationContext this is an interface class, to be exact, its actual implementation class is XmlWebApplicationContext . This is spring IoC container, which corresponds to the configuration specified by the Bean defined in web.xml context-param tag. After this initialization is complete IoC container, spring WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE to [] attribute Key, which is stored in the ServletContext , the ease of obtaining;

third step:

 Again, after contextLoaderListener listener initialized, start initialization Servlet web.xml configured, this servlet can configure multiple, the most common DispatcherServlet example, the servlet is actually a standard front-end controller for forwarding, matching each servlet processing request. DispatcherServlet context initialization will build their own IoC context, to hold spring mvc related bean. In establishing DispatcherServlet when their IoC context, will use the [start] WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE ServletContext root context before the acquisition (ie, the WebApplicationContext ) as the parent context of the context of their own (there is a parent attribute as a reference to Spring's ApplicationContext). With this context after the parent, then initialize the context of their own holdings. This DispatcherServlet initialization own work in the context of its initStrategies see approach, probably the work is to initialize the handler mapping, view resolution. The servlet context they hold default implementation class is XmlWebApplicationContext . After initialization is complete, spring in the name of the servlet related (here is not simply to servlet named Key, but through some transformations, specific self view source) properties for property Key, it will also be saved toServletContext in, for subsequent use. Thus each servlet to hold their own context, i.e., has its own independent space bean, while sharing the same respective servlet bean, i.e. those bean root context (step 2 context initialization) as defined.

By the above process starting this Spring, we can clearly understand the relationship between the ServletContext, WebApplicationContext, XmlWebApplicationContext, and DispatcherServlet context, and will WebApplicationContext on the ServletContext.

 
                                                 XmlWebApplicationContext architecture .png
 

1. ServletContext:

 Let me talk about the context of the web application ServletContext class. web container (such as tomcat, jboss, weblogic, etc.) startup, it creates a ServletContext object for each web application it represents the context of the current web application ( note: each web application has one and only create a ServletContext, a web application is a web project you ). All share a ServletContext servlet objects in a web, it can be achieved through communication between Servlet ServletContext object. In an object inherited from HttpServlet class, can be obtained by this.getServletContext.

2. WebApplicationContext:

 通过源码详细说明一下 第二步 的过程,web.xml(上图)中我们配置了ContextLoaderListener,该listener实现了ServletContextListener的contextInitialized方法用来监听Servlet初始化事件:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/SpringApplicationContext.xml</param-value>
</context-param>
<listener>  
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
</listener>
 

 由下面的源码可以发现初始化的是WebApplicationContext的IoC容器,它是一个接口类,其默认实现是XmlWebApplicationContext。

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }
    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }
    @Override
    public void contextInitialized(ServletContextEvent event) {
        initWebApplicationContext(event.getServletContext());
    }
    @Override
    public void contextDestroyed(ServletContextEvent event) {
        closeWebApplicationContext(event.getServletContext());
        ContextCleanupListener.cleanupAttributes(event.getServletContext());
    }
}

 

这是ContextLoaderListener中的contextInitialized()方法,这里主要是用initWebApplicationContext()方法来初始化WebApplicationContext。这里涉及到一个常用类WebApplicationContext:它继承自ApplicationContext,在ApplicationContext的基础上又追加了一些特定于Web的操作及属性。

 initWebApplicationContext(event.getServletContext()),进行了创建根上下文,并将该上下文以key-value的方式存储到ServletContext中。

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

    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 {
        // Store context in local instance variable, to guarantee that
        // it is available on ServletContext shutdown.
        if (this.context == null) {
            this.context = createWebApplicationContext(servletContext);
        }
        if (this.context instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
            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 ->
                    // determine parent for root web application context, if any.
                    ApplicationContext parent = loadParentContext(servletContext);
                    cwac.setParent(parent);
                }
                configureAndRefreshWebApplicationContext(cwac, servletContext);
            }
        }
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

        ClassLoader ccl = Thread.currentThread().getContextClassLoader();
        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 ex) {
        logger.error("Context initialization failed", ex);
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
        throw ex;
    }
    catch (Error err) {
        logger.error("Context initialization failed", err);
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
        throw err;
    }
}

 

 在initWebApplicationContext()方法中主要体现了WebApplicationContext实例的创建过程。首先,验证WebApplicationContext的存在性,通过查看ServletContext实例中是否有对应key的属性验证WebApplicationContext是否已经创建过实例。如果没有通过,createWebApplicationContext()方法来创建实例,并存放至ServletContext中。

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    Class<?> contextClass = determineContextClass(sc);
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
    }
    return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

 

 在createWebApplicationContext()方法中,通过BeanUtils.instanceClass()方法创建实例,而WebApplicationContext的实现类名称则通过determineContextClass()方法获得。

protected Class<?> determineContextClass(ServletContext servletContext) {
    String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
    if (contextClassName != null) {
        try {
            return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
        }
        catch (ClassNotFoundException ex) {
            throw new ApplicationContextException(
                    "Failed to load custom context class [" + contextClassName + "]", ex);
        }
    }
    else {
        contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
        try {
            return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
        }
        catch (ClassNotFoundException ex) {
            throw new ApplicationContextException(
                    "Failed to load default context class [" + contextClassName + "]", ex);
        }
    }
}

 

 determineContextClass()方法,通过defaultStrategies.getProperty()方法获得实现类的名称,而defaultStrategies是在ContextLoader类的静态代码块中赋值的。具体的途径,则是读取ContextLoader类的同目录下的ContextLoader.properties属性文件来确定的。

 That is, in the initialization process, the procedure first reads the attribute file directory with ContextLoader class ContextLoader.properties, and wherein the extraction according to the configuration to be achieved WebApplicationContext interface implementation class, and through reflection in accordance with examples of this class create.

 

In summary:

 When the action listener is started LoaderListener Web container ApplicationContext configuration of the automatic assembly. ServletContextListener because it implements this interface, in this listener web.xml configuration, when the container starts, it defaults to its implementation contextInitialized () WebApplicationContext instance initialization method, and placed in the ServletContext. Since the association ContextLoaderListener ContextLoader this class, the so the whole process is done by loading the configuration ContextLoader.

3. DispatcherServlet

 

 
FIG DispatcherServlet structure .png

 Detailed third step , after the listener contextLoaderListener initialized, the DispatcherServlet start initialization, the following initialization method for the source:

 

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

 

需要做的八件事情如下所述:

  • initMultipartResolver:初始化MultipartResolver,用于处理文件上传服务,如果有文件上传,那么就会将当前的HttpServletRequest包装成DefaultMultipartHttpServletRequest,并且将每个上传的内容封装成CommonsMultipartFile对象。需要在dispatcherServlet-servlet.xml中配置文件上传解析器。
  • initLocaleResolver:用于处理应用的国际化问题,本地化解析策略。
  • initThemeResolver:用于定义一个主题。
  • initHandlerMapping:用于定义请求映射关系。
  • initHandlerAdapters:用于根据Handler的类型定义不同的处理规则。
  • initHandlerExceptionResolvers:当Handler处理出错后,会通过此将错误日志记录在log文件中,默认实现类是SimpleMappingExceptionResolver。
  • initRequestToViewNameTranslators:将指定的ViewName按照定义的RequestToViewNameTranslators替换成想要的格式。
  • initViewResolvers:用于将View解析成页面。
  • initFlashMapManager:用于生成FlashMap管理器。

 通过查看DispatcherServlet(源码内容太多就不往上放了),DispatcherServlet继承自FrameworkServlet,而FrameworkServlet是继承自HttpServletBean的,HttpServletBean又继承了HttpServlet。这是因为DispatcherServlet本身就得是一个Servlet,且含有doGet()和doPost()方法,Web容器才可以调用它,所以它的顶级父类为含有这俩方法的HttpServlet。具体的Web请求,会经过FrameServlet的processRequest方法简单处理后,紧接着调用DispatcherServlet的doService方法,而在这个方法中封装了最终调用处理器的方法doDispatch。这也意味着,DispatcherServlet的最主要的核心功能由doService和doDispatch实现的。

DispatcherServlet类的方法大致可分为三种:

  • 初始化相关处理类的方法。
  • 响应Http请求的方法。
  • 执行处理请求逻辑的方法。
核心方法 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;
            Object dispatchException = null;

            try {
                processedRequest = this.checkMultipart(request);
                multipartRequestParsed = processedRequest != 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(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } catch (Exception var20) {
                dispatchException = var20;
            } catch (Throwable var21) {
                dispatchException = new NestedServletException("Handler dispatch failed", var21);
            }

            this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
        } catch (Exception var22) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
        } catch (Throwable var23) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
        }

    } finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        } else if (multipartRequestParsed) {
            this.cleanupMultipart(processedRequest);
        }

    }
}

 

DispatcherServlet处理该类请求的步骤:
  1. 在接收到请求后,通过几级Servlet类型的父类的处理,先调用doService在request中设置一些必需的参数。最终会调用DispatcherServlet的doDispatch方法。
  2. 在doDispatch方法中,首先检测request是否包含多媒体类型(如File文件上传),然后将检测后的request转换为processedRequest对象。之后检测processedRequest对象是否为原始request(如果是,即原来的request不包含多媒体信息),然后将boolean结果赋给multipartRequestParsed变量(若multipartRequestParsed为true,在最后会清除processedRequest对象中的多媒体信息)。
  3. 十分重要的一步,就是通过调用处理器映射器查找Handler。调用getHandler来获取相关的处理器对象。在getHandler方法中,利用处理器映射器HandlerMapping通过request来获取一个包含Handler处理器本身和其前后拦截器interceptor的处理器执行链HandlerExecutionChain对象。
  4. 通过HandlerExecutionChain对象获取具体的Handler处理器对象,此时使用getHandlerAdapter方法获取可以处理类型的处理器适配器HandlerAdapter对象。
  5. 调用HandlerAdapter对象的handle方法,将可能带有多媒体信息的processRequest对象,原始request对象,以及Handler处理器本身作为参数传入,handle方法会根据这些参数去执行开发者自己开发的Handler的相关
    请求处理逻辑,并返回含有反馈信息和结果视图信息的ModelAndView对象。
  6. 获得ModelAndView对象后,会进行视图渲染,将model数据填充到request域。在processDispatchResult方法中会对ModelAndView对象进行处理。而在processDispatchResult方法中包含一个render方法,其参数为ModelAndView对象以及request和response对象。在render方法中,通过resolveViewName会获取到实际需要使用的视图View对象,这个对象的具体类型是由XXX决定的。然后就会执行具体的View对象的render方法来完成数据的显示过程。这里举一个视图类型的例子,他在render方法中具体执行了以下逻辑来绑定结果数据和视图:
protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception{
  //遍历model里面的数据,填充到request域
  for (Map.Entry<String, Object> entry : model.entrySet()){
      String modeName = entry.getKey();
      Object modelValue = entry.getValue();
      if(modelValue != null){
          request.setAttribute(modelName, modelValue);
          if(logger.isDebugEnabled()){
              logger.debug("Added model object '"
                  + modelName + "' of type [" + modelValue.getClass.getName()
                  +"] to request in view with name '" + getBeanName() + "'");
          } else{
             request.removeAttribute(modelName);
            if(logger.isDebugEnabled()){
                logger.debug("Remove modek object '" +modelName +
                    "' from request in view with name '" +getBeanName() + "'");
            } 
         }
      }
  }
}

 

 可以看到,在这里会把ModelAndView中model的数据遍历出来,分为key和value,并且将数据设置在request的attribute域中。之后加载页面时就可以使用标签在request域中获取返回参数了。

 
                                                                   DispatcherServlet初始化时序图.png

Guess you like

Origin www.cnblogs.com/by-my-blog/p/11831053.html