Creation of Spring Container for Spring Boot

1 web.xml

In a Java Web system, using the Spring framework, a listener ContextLoaderListener needs to be configured in web.xml, and the creation of the Spring context starts with this listener.

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

2 Create a Spring context in ContextLoaderListener

ContextLoaderListener implements the ServletContextListener interface. This listener will be initialized after the ServletContext is created and before the filter and servlet are initialized. The contextInitialized method will be automatically called during initialization, and the Spring context will be initialized in this method.

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }

    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }

    public void contextInitialized(ServletContextEvent event) {
        // 初始化 Spring 上下文
        this.initWebApplicationContext(event.getServletContext());
    }

    public void contextDestroyed(ServletContextEvent event) {
        this.closeWebApplicationContext(event.getServletContext());
        ContextCleanupListener.cleanupAttributes(event.getServletContext());
    }
}

3 ContextLoader's initWebApplicationContext method

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        // 如果 Servlet 中已经存在 Spring 上下文抛异常,启动失败
        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) {
                    // 创建 Spring 上下文,并赋值给 context 属性
                    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);
                        }

                        // 此方法中将对 Bean 进行初始化化
                        this.configureAndRefreshWebApplicationContext(cwac, servletContext);
                    }
                }

                // Spring 上下文初始化成功,将其保存到 Web 容器上下文 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 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;
            }
        }
    }

4 ContextLoader's createWebApplicationContext method

Obtain the Class object of the implementation class of WebApplicationContext in the createWebApplicationContext method

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
        // 返回的是 XmlWebApplicationContext 的 Class 对象
        Class<?> contextClass = this.determineContextClass(sc);
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        } else {
            // 通过反射实例化 XmlWebApplicationContext 并返回
            return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
        }
}

5 ContextLoader's determineContextClass method

 protected Class<?> determineContextClass(ServletContext servletContext) {
        String contextClassName = servletContext.getInitParameter("contextClass");
        if (contextClassName != null) {
            try {
                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
            } catch (ClassNotFoundException var4) {
                throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4);
            }
        } else {
            // 获取 WebApplicationContext 实现类的全路径名,defaultStrategies 是一个 Properties 类型常量,在静态代码块中初始化 
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());

            try {
                // 根据 contextClassName 加载器字节码文件到方法区,并将他在虚拟机堆中的 Class 对象返回
                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
            } catch (ClassNotFoundException var5) {
                throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5);
            }
        }
 }

When defaultStrategies is initialized, it will read the property file ContextLoader.properties in the same directory of ContextLoader, which contains the full path name of the implementation class of WebApplicationContext

static {
        try {
            ClassPathResource resource = new ClassPathResource("ContextLoader.properties", ContextLoader.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        } catch (IOException var1) {
            throw new IllegalStateException("Could not load 'ContextLoader.properties': " + var1.getMessage());
        }

        currentContextPerThread = new ConcurrentHashMap(1);
    }

The ContextLoader.properties file is in the org.springframework.web.context package

# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324836804&siteId=291194637