Spring 启动之 Spring 容器的创建

1 web.xml

在一个 Java Web 系统中,使用 Spring 框架,需要在 web.xml 中配置一个监听器 ContextLoaderListener,而 Spring 上下文的创建起始于这个监听器。

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

2 ContextLoaderListener 中创建 Spring 上下文

ContextLoaderListener 实现了 ServletContextListener 接口,此监听器将在 ServletContext 创建后,过滤器和 Servlet 初始化之前进行初始化,初始化时 contextInitialized 方法将自动被调用,在这个方法中开始初始化 Spring 上下文

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 的 initWebApplicationContext 方法

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 的 createWebApplicationContext 方法

createWebApplicationContext 方法中获取 WebApplicationContext 的实现类的 Class 对象

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 的 determineContextClass 方法

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

defaultStrategies 初始化时将读取 ContextLoader 同一目录下的属性文件 ContextLoader.properties,此文件中有 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);
    }

ContextLoader.properties 文件在 org.springframework.web.context 包中

# 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

猜你喜欢

转载自blog.csdn.net/li90hou/article/details/80065320