Spring在设计时主要针对接口进行开发(顶级类均为接口BeanDefinition、Resource、BeanFactory等),为满足各种场景的需要,其源码中包含了各种实现。想要了解整个源码实在是是一件很困难且耗费时间的一件事。因此本文只涉及到了常用配置的实现(Spring+SpringMVC),且在启动时涉及到的部分。SpringMVC的启动以后再谈。
Step 1:容器加载并解析web.xml
- 当启动一个WEB项目时,容器(JBOSS/Tomcat/Weblogic等)首先会加载并解析项目中的web.xml文件。
- 容器会创建一个应用上下文(ServletContext)。顾名思义,ServletContxt就是为了方便在整个Servlet应用中共享数据。例如,web.xml中<context-param></context-param>标签就是为了应用中动态获取对应属性。Spring配置文件的位置也是通过该标签设置的。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-context.xml</param-value>
</context-param>
- 容器实例化web.xml中<listener></listener>配置的类(即ContextLoaderListener),实例化后,容器会通过监听机制执行其**contextInitialized()**方法正式揭开Spring的启动流程
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Step 2:初始化Spring上下文WebApplicationContext
Spring的启动开始于容器回调ContextLoaderListener的**contextInitialized()**方法。Spring启动时默认的WebApplicationContext实现类是XmlWebApplicationContext。
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// 判断当前ServletContext是否初始化过WebApplicationContext
// key="org.springframework.web.context.WebApplicationContext.ROOT"
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!");
}
long startTime = System.currentTimeMillis();
try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
// 若当前上下文未初始化WebApplicationContext,则进行初始化,此处实例化的实现类是XmlWebApplicationContext
// 该过程仅仅是类的实例化,未做任何其他处理,实现类为XmlWebApplicationContext。
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
// WebApplicationContext实例化后处于未激活状态,即cwac.isActive()=false
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.
// Spring应用上下文,此处返回null
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
// 真正的IOC容器初始化入口,即调用refresh()的方法,了解SpringIOC容器的应该知道refresh()的重要性
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
// 在Servlet上下文中添加属性,表示该上下文已经初始化过WebApplicationContext,与该方法入口处的校验对应
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) {
// 此处的目的是 方便Spring其他模块在依赖Spring时 能够快速获取到对应的Spring上下文WebApplicationContext
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;
}
}
Step 3: 配置Spring上下文WebApplicationContext
配置内容主要包含配置上下文ID、确定Spring配置文件位置、关联Spring上下文和Servlet上下文等。WebApplicationContext作为Spring上下文,同时其实现了BeanFactory接口,即其可以作为IOC容器使用,但其实现方式却是通过内部持有一个BeanFactory的实现类来实现的。
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {(**DefaultListableBeanFactory**)
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
// 若配置中指定了id,则设置为配置中的id
wac.setId(idParam);
}
else {
// 生成上下文的Id, 以区分不同的IOC容器(Spring和SpringMVC容器可以同时存在)
// id=org.springframework.web.context.WebApplicationContext:
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
// 将Spring应用上下文和Servlet上下文进行关联
wac.setServletContext(sc);
// CONFIG_LOCATION_PARAM=contextConfigLocation即web.xml中的Spring配置
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
// 指定Spring的配置文件,默认为/WEB-INF/applicationContext.xml
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
// 自定义化Spring上下文,通过实现ApplicationContextInitializer来实现
customizeContext(sc, wac);
// IOC容器的启动入口
wac.refresh();
}
Step 4: IOC容器初始化
此处IO容器指的是Spring上下文内部持有的BeanFactory的实现类的初始化。默认实现为DefaultListableBeanFactory。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 2.3.1.设置启动时间 激活状态 初始化PropertySources等
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 2.3.2.刷新自身持有的BeanFactory
// 此过程包含4步:
// 1.存在旧BeanFactory时,则销毁旧BeanFactory。
// 2.实例化新的BeanFactory,默认实现类为DefaultListableBeanFactory
// 3.初始化新BeanFactory,配置自定义属性等
// 4.向容器注册BeanDefinition即Spring Bean。此过程会读取web.xml中的contextConfigLocation配置的文件,将依据其中的配置解析成BeanDefinition
// 步骤4中只加载定义BeanDefinition,未实例化Bean
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 配置BeanFactory的标准特征、类加载器和默认的后置处理器
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 配置环境Environment和初始化BeanScop等
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 执行配置的BeanFactory的后置处理器(执行必须发生在单例Bean被使用前)
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 注册Bean的后置处理器(区别于BeanFactory的后置处理器)
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 初始化消息源
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 初始化themeSource
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 实例化单例模式的Bean,非单例模式在getBean()时实例化
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// IOC容器启动完成后,调用生命周期处理器并发布事件通知(实现ApplicationListener子类会被回调)等
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
// 异常时,销毁已实例化的Bean.
destroyBeans();
// Reset 'active' flag.
// 设置当前Spring上下文的状态为false.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
// 清空在整个Spring上下文加载过程中的缓存,主要是指反射。
resetCommonCaches();
}
}
}