Start and initialize Dispatcher Servlet

Dispatcher Servlet is known to all who have used Spring MVC. Here's how to start and initialize the Servlet. As a servlet, the start of Dispatcher Servlet is related to the start process of Serlvet. In Serlvet's initialization process, Serlvet's init method is called for initialization. The source code for this initialization process in the base class HttpServletBean of Dispatcher Servlet is as follows:

public final void init() throws ServletException {
    if (logger.isDebugEnabled()) {
        logger.debug("Initializing servlet '" + getServletName() + "'");
    }

    // Obtain Servlet Initialization parameters, right Bean Property Configuration
    try {
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
        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;
    }

    // Calling subclasses initServletBean Method for specific initialization
    initServletBean();

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

// initServletBean This initialization work is located at FrameworkServlet In class
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();

    // Here initializes the context
    try {
        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");
    }
}

protected WebApplicationContext initWebApplicationContext() {
    // call WebApplicationContextUtils To get the root context, it is saved in ServletContext in
    // Use it as the current MVC Parental context
    WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;

    if (this.webApplicationContext != null) {
        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) {
        wac = findWebApplicationContext();
    }
    if (wac == null) {
        wac = createWebApplicationContext(rootContext);
    }

    if (!this.refreshEventReceived) {
        onRefresh(wac);
    }

    // Save the currently established context to ServletContext In the Servlet Name related
    if (this.publishContext) {
        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;
}

At the beginning of initialization, you need to read the Bean attribute parameters configured in ServletContext, which are set in the Web container initialization parameters of web.xml.

The initialization of the IOC container held by the Dispatcher Servlet is then performed, in which a new context is established, and the context held by the Dispatcher Servlet is set to the context of the root context. Understandably, the root context is a context corresponding to the web application, while the context held by the Dispatcher Servlet is a context corresponding to the Servlet. A web application can accommodate multiple servlets; correspondingly, for a context system applied in a web container, a root context can be used as a parent context for many Serlvet contexts. Understanding this is helpful in setting up and retrieving IOC container beans in a web environment, because when an IOC container getBean is sent to its parent context, the IOC container first goes to getBean, in other words, the beans defined in the root context can be shared and shared by the contexts held by various servlets. After the context held by the Dispatcher Servlet is established, it also needs to complete the initialization operation as other IOC containers. This initialization operation is done by refresh method. Finally, the Dispatcher Servlet names its own context and sets it to the context of the web window (ServletContext), which is the name and Servl of the Dispatcher Servlet set in web.xml. The name of ET is related to ensure the uniqueness of this context in the context system of web environment.

After the above code is executed, the context of the Servlet is established. The process of obtaining the root context is implemented in Web Application Context Utils. The source code is as follows:

public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
    // ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE This attribute represents the root context
    // stay ContextLoaderListener The initialization process is established and set to ServletContext in
    return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}

This root context is set to the ServletContext using the attribute ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, and for the bean configuration file of the IOC container, ContextLoader is also set. The default location is in the / WEB-INF/applicationContext.xml file, because this root context is the parent context of the context established by Dispatcher Servlet. The beans managed in the root context can be used in the context of the Dispatcher Servlet, but not vice versa. When the beans are retrieved from the IOC container by getBean, they will first try to retrieve the beans from their parent IOC container.

Go back to the Framework Servlet and continue to see how the context of the Dispatcher Servlet is established. The source code is as follows:

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
    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 + "]");
    }
    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");
    }
    
    // Instantiate the specific context object you need and set properties for that context object
    // Here we use the DEFAULT_CONTEXT_CLASS,this DEFAULT_CONTEXT_CLASS Set to XmlWebApplicationContext.class,
    // So in DispatcherServlet Used in IOC Container is XmlWebApplicationContext
    ConfigurableWebApplicationContext wac =
            (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

    wac.setEnvironment(getEnvironment());
    // Setting parental context (that is, root context)
    wac.setParent(parent);
    wac.setConfigLocation(getContextConfigLocation());

    configureAndRefreshWebApplicationContext(wac);

    return wac;
}

To create the context of a Dispatcher Servlet, you need to pass the root context to it as a parameter, then use reflection to instantiate the context object and set parameters for it. The context object is the Xml Web Application Context object, which is set in the DEFAULT_CONTEXT_CLASS parameter and allowed to be used by BeanUtils by default configuration. After the instantiation is completed, the Xml Web Application Context object is also included in the DEFAULT_CONTEXT_CLASS parameter. Some basic configurations need to be set up for this context object, including its parent context, the location of the file defined by the Bean, etc. After configuring, the final initialization of the IOC container is completed by calling the refresh method of the IOC container.

After a series of operations of the web container above, Spring MVC can play its role on the basis of the establishment and initialization of the context system. At this point, Dispatcher Servlet holds an IOC container named after its own Servlet. When the IOC container is established, it means that Dispatcher Servlet has its own Bean definition space. This creates conditions for configuring beans in MVC using individual XML files. After initialization, the specific implementation of Spring MVC and the implementation of ordinary Spring application program are not very good. In the process of initialization of Dispatcher Servlet, the initialization call to Handler Mapping is used as trigger point. You can see the method invocation diagram of Spring MVC module initialization in the following figure.

This invocation relationship is initially triggered by the init method of the HttpServletBean, which is a subclass of the HttpServlet, and then initializes the IOC container in the Framework Servlet, a subclass of the HttpServletBean. In this initialization method, the initStrategies method of Dispatcher Servlet is invoked. In this initStrategies method, the initialization of the entire Spring MVC framework is started. Chemical work.

From the method invocation diagram above, it can be seen that the initialization of MVC is completed in initStrategies of Dispatcher Servlet, including the initialization of various MVC framework implementation elements, such as LocalResolver for internationalization support, ViewResolver for view generation, etc. The source code is as follows:

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

The names of the above initialization methods should be well understood. Here we take the common Handler Mapping as an example to illustrate the implementation of initHandler Mappings (). The function of Mapping is to find the corresponding Controller controller for HTTP requests. Handler Mappings completes the definition and configuration of Controller in MVC. The source code of Handler Mappings initialization process in Dispatcher Servlet is as follows:

private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;

    // All of them are imported here. HandlerMapping Bean,These ones here Bean Can be in the current DispatcherServlet Of IOC
    // In a container, or in the context of its parents, this detectAllHandlerMappings The default value is set to true,
    // That is, by default, from all IOC Acquisition in containers
    if (this.detectAllHandlerMappings) {
        Map<String, HandlerMapping> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
            OrderComparator.sort(this.handlerMappings);
        }
    }
    else {
        try {
            // You can use the name from the current IOC Pass through the container getBean Obtain HandlerMapping
            HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
            this.handlerMappings = Collections.singletonList(hm);
        }
        catch (NoSuchBeanDefinitionException ex) {
        }
    }

    // If not found HandlerMappings,So you need to Servlet Set the default HandlerMappings,
    // These default values can be set DispatcherServlet.properties in
    if (this.handlerMappings == null) {
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
        if (logger.isDebugEnabled()) {
            logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
        }
    }
}

During the initialization of Handler Mapping, the handler Mapping configured in the Bean configuration file is retrieved from the IOC container. Through the above reading process, the handler Mappings variable has acquired the mapping relationship configured in BeanDefinition. Other initialization processes are similar to handler Mappings, which read configuration from the IOC container, so the MVC initialization process is based on the completion of the initialization of the IOC container. After performing other initialization operations, the whole initialization process is basically completed.

Keywords: Java Spring xml Attribute

Added by M4F on Tue, 04 Jun 2019 04:40:22 +0300