SpringIOC初始化过程源码跟踪及学习
Spring版本 4.3.2,ssm框架
代码过宽,可以shift + 鼠标滚轮 左右滑动查看
web.xml
<!--配置获取项目的根路径,java类中使用System.getProperty("web.root")-->
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>web.root</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.WebAppRootListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
IOC容器初始化从ContextLoaderListener类开始,这个监听器是ServletContextListener的子类,在web.xml配置后会被容器(为避免和IOC容器混淆,后文用tomcat代称)调用。该类继承关系如下:
tomcat初始化ContextLoaderListener类时会先调用其父类ContextLoader的静态代码块
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
// 加载属性文件默认策略的实现,当前处于完全内部环境,不能由开发者去自定义
try {
// DEFAULT_STRATEGIES_PATH 常量,指向该类同一目录层级下的ContextLoader.properties文件
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
//用流读取文件中的key-value对,在这个地方确定默认上下文的Class
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}
ContextLoader.properties文件中内容:
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
默认策略读取完成,此策略确定未指定上下文时的默认类型,为XmlWebApplicationContext。
如果web.xml中有配置WebAppRootListener监听器,根据web.xml中的顺序,先调用WebAppRootListener的contextInitialized方法。
public void contextInitialized(ServletContextEvent event) {
WebUtils.setWebAppRootSystemProperty(event.getServletContext());
}
public static void setWebAppRootSystemProperty(ServletContext servletContext) throws IllegalStateException {
Assert.notNull(servletContext, "ServletContext must not be null");
//获取项目在主机上的完整路径root
String root = servletContext.getRealPath("/");
if (root == null) {
throw new IllegalStateException(
"Cannot set web app root system property when WAR file is not expanded");
}
//WEB_APP_ROOT_KEY_PARAM为常量:webAppRootKey。在web.xml中获取key为webAppRootKey的value值
String param = servletContext.getInitParameter(WEB_APP_ROOT_KEY_PARAM);
//DEFAULT_WEB_APP_ROOT_KEY为常量:webapp.root,如没有指定WEB_APP_ROOT_KEY_PARAM则取此常量
String key = (param != null ? param : DEFAULT_WEB_APP_ROOT_KEY);
//不管拿的是DEFAULT_WEB_APP_ROOT_KEY还是WEB_APP_ROOT_KEY_PARAM,以其value作为key,找到对应的root
String oldValue = System.getProperty(key);
//确保root唯一
if (oldValue != null && !StringUtils.pathEquals(oldValue, root)) {
throw new IllegalStateException(
"Web app root system property already set to different value: '" +
key + "' = [" + oldValue + "] instead of [" + root + "] - " +
"Choose unique values for the 'webAppRootKey' context-param in your web.xml files!");
}
//设置root
System.setProperty(key, root);
servletContext.log("Set web app root system property: '" + key + "' = [" + root + "]");
}
WebAppRootListener监听器初始化任务结束,然后调用ContextLoaderListener监听器的contextInitialized方法
/**
* Initialize the root web application context.
*
* 初始化根上下文
*/
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
initWebApplicationContext(零 )
initWebApplicationContext方法在ContextLoaderListener的父类ContextLoader中
/**
* Initialize Spring's web application context for the given servlet context,
* using the application context provided at construction time, or creating a new one
* according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
* "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
*
* 通过给定的 servlet context 初始化 Spring 的 web application context,
* 这个 application context 要么是在构造时被提供,要么是根据 contextClass
* 和 contextConfigLocation 两个上下文参数重新创建的。
*/
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// servlet context 中只能有一个 root web application context。
// root web application context 初始化完成后会放入 servlet context 中
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
// 不能初始化 context ,因为已经有一个 root web application context存在
// 检查你是否在你的web.xml中定义了多个ContextLoader
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);
// 初始化 spring root WebApplicationContext
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
// Root WebApplicationContext 初始化开始
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.
// 在这个实例变量中保存 context,
// 以确保在 ServletContext 关闭时能够用到
if (this.context == null) {
// 1.创建 WebApplicationContext
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
// 默认 WebApplicationContext 没有激活
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
// 此时 context 还没有被刷新 -> 提供设置 parent context 、application context id
// 等服务。
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
// 2.这个 context 实例被注入时没有一个明确的 parent -> 如果有的话,
// 需要为 root web application context 确定 parent
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
// 3.配置并刷新 WebApplicationContext(超级巨大的方法)
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
//将 root web application context 添加到 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;
}
}
1.createWebApplicationContext
从标1的方法开始,这个方法用来创建 root web application context ,并用ContextLoader的context变量接收
// 1.创建 WebApplicationContext
this.context = createWebApplicationContext(servletContext);
// 看下ContextLoader类的context属性
/**
* The root WebApplicationContext instance that this loader manages.
*
* 由ContextLoader管理的 root web application context.
*/
private WebApplicationContext context;
/**
* Instantiate the root WebApplicationContext for this loader, either the
* default context class or a custom context class if specified.
* <p>This implementation expects custom contexts to implement the
* {@link ConfigurableWebApplicationContext} interface.
* Can be overridden in subclasses.
* <p>In addition, {@link #customizeContext} gets called prior to refreshing the
* context, allowing subclasses to perform custom modifications to the context.
*
* 为这个 loader 实例化 root WebApplicationContext ,
* 要用采用默认的 context class,要么使用指定的自定义的 context class。
* 对于自定义的 contexts class 来说,
* 希望实现 ConfigurableWebApplicationContext 接口,也可以重写他的子类。
* 另外,customizeContext方法 会在 context 被刷新前调用,
* 允许子类执行对 context 的自定义修改。
*/
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
// 确定 Context Class
Class<?> contextClass = determineContextClass(sc);
// 确保自定义 context 是 ConfigurableWebApplicationContext 的子类
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
// 实例化 root WebApplicationContext
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
/**
* Return the WebApplicationContext implementation class to use, either the
* default XmlWebApplicationContext or a custom context class if specified.
*
* 返回 WebApplicationContext 接口的实现,
* 要么使用默认的 XmlWebApplicationContext ,要么使用自定义的 context
*/
protected Class<?> determineContextClass(ServletContext servletContext) {
// CONTEXT_CLASS_PARAM -> contextClass
// 如果web.xml中配置了contextClass这个属性,那么就取自定义context Class
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 {
// 如果没有配置自定义 context Class,
// 则取默认策略中的 context Class,也就是 XmlWebApplicationContext
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);
}
}
}
这样标1的方法也就执行完了。看一下XmlWebApplicationContext的继承关系:
2.loadParentContext
跟踪标记2的方法。
此方法在ContextLoader类中实现。
// 2.这个 context 实例被注入时没有一个明确的 parent -> 如果有的话,
// 需要为 root web application context 确定 parent
ApplicationContext parent = loadParentContext(servletContext);
/**
* Template method with default implementation (which may be overridden by a
* subclass), to load or obtain an ApplicationContext instance which will be
* used as the parent context of the root WebApplicationContext. If the
* return value from the method is null, no parent context is set.
* <p>The main reason to load a parent context here is to allow multiple root
* web application contexts to all be children of a shared EAR context, or
* alternately to also share the same parent context that is visible to
* EJBs. For pure web applications, there is usually no need to worry about
* having a parent context to the root web application context.
* <p>The default implementation uses
* {@link org.springframework.context.access.ContextSingletonBeanFactoryLocator},
* configured via {@link #LOCATOR_FACTORY_SELECTOR_PARAM} and
* {@link #LOCATOR_FACTORY_KEY_PARAM}, to load a parent context
* which will be shared by all other users of ContextsingletonBeanFactoryLocator
* which also use the same configuration parameters.
*
* 一个默认实现的模板方法(可能被子类覆盖),
* 用来加载和获得一个 ApplicationContext 的实例,这个实例被用来
* 作为 root WebApplicationContext 的 parent context 。
* 如果此方法返回的是null,那么 root WebApplicationContext 就没有 parent context 。
* 加载 parent context 的主要原因是为了让多个 root web application contexts
* 成为共享 EAR context 的 children,
* 或者共享一个对 EJBs 可见的 parent context。
* 为了web applications 的纯洁性,通常不需要关心 root web application context 的 parent context。
* 默认实现使用 ContextSingletonBeanFactoryLocator,通过两个参数去配置,去加载一个被所有
* ContextsingletonBeanFactoryLocator 其他用户所共享的 parent context,它们也使用一样的配置参数。
*/
protected ApplicationContext loadParentContext(ServletContext servletContext) {
ApplicationContext parentContext = null;
String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);
String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);
// 这里没有配置这两个参数,所以此 context 没有 parent
if (parentContextKey != null) {
// locatorFactorySelector may be null, indicating the default "classpath*:beanRefContext.xml"
BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
Log logger = LogFactory.getLog(ContextLoader.class);
if (logger.isDebugEnabled()) {
logger.debug("Getting parent context definition: using parent context key of '" +
parentContextKey + "' with BeanFactoryLocator");
}
this.parentContextRef = locator.useBeanFactory(parentContextKey);
parentContext = (ApplicationContext) this.parentContextRef.getFactory();
}
return parentContext;
}
3.configureAndRefreshWebApplicationContext
跟踪标记3的方法。
此方法在ContextLoader类中实现。
// 3.配置并刷新 WebApplicationContext(超级巨大的方法)
configureAndRefreshWebApplicationContext(cwac, servletContext);
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
// 在类AbstractApplicationContext中,定义了 context 的id
// private String id = ObjectUtils.identityToString(this);
// id是 context 的唯一标识
// identityToString 方法返回的是 context 的类名 + "@" + 十六进制的哈希值
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
// 这个 application context id 仍然设置为他的初始默认值-->基于可利用的信息分配给他一个更有用的id
// 如果 servlet context 中设置了 CONTEXT_ID_PARAM -> contextId属性,
// 那么就采用这个作为id
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
// Generate default id...
// 否则生成默认的id
// "org.springframework.web.context.WebApplicationContext:" + 项目名
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);
// CONFIG_LOCATION_PARAM -> contextConfigLocation,
// 从sevletContext中拿的这个属性,就是我们经常在web.xml中配置的属性:
// classpath:spring/applicationContext.xml
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
// 以数组的形式将 contextConfigLocation 保存在
// root web application context 的 configLocations 属性中,
// 保存时会先对路径进行解析,替换占位符,而这个操作需要 Environment 对象来完成。
// context 中有属性 environment,environment 默认为 StandardServletEnvironment 实现,
// 实例化 StandardServletEnvironment 时其父类 AbstractEnvironment
// 会调用 customizePropertySources 方法,
// 这个方法会将 systemEnvironment、systemProperties 啥的键值对以及jndiProperties保存在实例中,
// 后续还会将 servletContextInitParams 、servletConfigInitParams 等属性保存进来
wac.setConfigLocation(configLocationParam);
}
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
// 当 context 被刷新时,wac 的 environment 的 initPropertySources 方法在任何情况下都会被调用。
// 之所以这么急切的调用是为了确保 servlet 属性源在一些 post-processing 中或者发生在refresh方法
// 之前的初始化中能够到位使用。
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
//将 servletContextInitParams、servletConfigInitParams 等属性保存进 environment 对象中
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
// 3.1对 Context 的自定义操作
customizeContext(sc, wac);
// 3.2整个IOC的重点,内容非常多,里面的每个方法都会分一篇文章单独讲
wac.refresh();
}
3.1 customizeContext
跟踪标记3.1的方法。
此方法在ContextLoader类中实现。
// 3.1对上下文的自定义操作
customizeContext(sc, wac);
/**
* Customize the {@link ConfigurableWebApplicationContext} created by this
* ContextLoader after config locations have been supplied to the context
* but before the context is <em>refreshed</em>.
* <p>The default implementation {@linkplain #determineContextInitializerClasses(ServletContext)
* determines} what (if any) context initializer classes have been specified through
* {@linkplain #CONTEXT_INITIALIZER_CLASSES_PARAM context init parameters} and
* {@linkplain ApplicationContextInitializer#initialize invokes each} with the
* given web application context.
* <p>Any {@code ApplicationContextInitializers} implementing
* {@link org.springframework.core.Ordered Ordered} or marked with @{@link
* org.springframework.core.annotation.Order Order} will be sorted appropriately.
*
* 当 config locations 被提供给 context 后,
* 由此 ContextLoader 创建的 ConfigurableWebApplicationContext 可以进行自定义化,
* 但是必须在 context 被刷新之前进行。
* 这个默认的实现,determineContextInitializerClasses 方法,
* 通过两个参数以及给定的 web application context 确定了 context initializer classes,这两个参数分别是
* CONTEXT_INITIALIZER_CLASSES_PARAM(上下文初始化参数)和ApplicationContextInitializer(调用每一个)。
* 任何实现了 Ordered 或者有 Order 注解的 ApplicationContextInitializers 都将被适当的排序
*/
protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
// 3.1.1web.xml中没有自定义参数,所以此处返回空list
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
determineContextInitializerClasses(sc);
// 没有自定义操作,所以跳过。有则实例化 Initializer
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
Class<?> initializerContextClass =
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
throw new ApplicationContextException(String.format(
"Could not apply context initializer [%s] since its generic parameter [%s] " +
"is not assignable from the type of application context used by this " +
"context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
wac.getClass().getName()));
}
this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
}
// 如果有多个 contextInitializers,会根据 Order 做一个排序
AnnotationAwareOrderComparator.sort(this.contextInitializers);
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
// contextInitializers 对 context 进行初始化
initializer.initialize(wac);
}
}
3.1.1 determineContextInitializerClasses
跟踪标记3.1.1的方法。
此方法在ContextLoader类中实现。
// 3.1.1web.xml中没有定义,所以此处返回空list
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
determineContextInitializerClasses(sc);
/**
* Return the {@link ApplicationContextInitializer} implementation classes to use
* if any have been specified by {@link #CONTEXT_INITIALIZER_CLASSES_PARAM}.
*
* 如果指定了 CONTEXT_INITIALIZER_CLASSES_PARAM 参数,
* 将会返回 ApplicationContextInitializer 接口的实现类的Class对象以供使用
*/
protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>
determineContextInitializerClasses(ServletContext servletContext) {
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes =
new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();
//是否有自定义GLOBAL_INITIALIZER_CLASSES_PARAM参数,没有跳过
String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM);
if (globalClassNames != null) {
for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
classes.add(loadInitializerClass(className));
}
}
//是否有自定义CONTEXT_INITIALIZER_CLASSES_PARAM参数,没有跳过
String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
if (localClassNames != null) {
for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) {
classes.add(loadInitializerClass(className));
}
}
//web.xml中没有定义,所以此处返回空list
return classes;
}
3.2 refresh
跟踪标记3.2的方法。
此方法在AbstractApplicationContext类中实现。
// 3.2刷新操作
wac.refresh();
/**
* Load or refresh the persistent representation of the configuration,
* which might an XML file, properties file, or relational database schema.
* <p>As this is a startup method, it should destroy already created singletons
* if it fails, to avoid dangling resources. In other words, after invocation
* of that method, either all or no singletons at all should be instantiated.
*
* 加载或者刷新配置的持久性表示,
* 这些配置可能在xml文件上,properties文件上,或者相关数据库上。
* 因为这是一个启动方法,所以如果他失败的话那么已经被创建的单例会被销毁,避免占用资源。
* 换句话说,在这个方法被调用之后,要么所有单例都被实例化,要么全部都没有。
*/
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
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.
destroyBeans();
// Reset 'active' flag.
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...
resetCommonCaches();
}
}
}
这上面的方法会一个个跟踪。
未完····
参考:
prepareRefresh方法:https://www.jianshu.com/p/c7364aeb0443
obtainFreshBeanFactory方法:https://www.jianshu.com/p/144af98965d9
prepareBeanFactory方法:https://www.jianshu.com/p/3468118a31f9
postProcessBeanFactory方法:https://www.jianshu.com/p/c05aea93b939
总结
- ContextLoaderListener监听器在web.xml中配置,是ServletContextListener子类,由容器调用
- 父类静态代码块读取默认策略文件,确定默认下ApplicationContext的类型
- (零)开始初始化 web application context
——————————————————————————————————
- (零)
- 1.创建 web application context 。web.xml中有指定contextClass,则取自定义Context类型,需要实现ConfigurableWebApplicationContext接口,没有指定则取默认策略类型
- 2.有配置参数就实例化一个 ApplicationContext ,这个实例作为 web application context 的 parent context
- 3.配置并刷新 WebApplicationContext
——————————————————————————————————
- 3
- 配置 application context 的 id、contextConfigLocation等,这个过程会初始化Environment对象
- 对 context 进行自定义操作,自定义初始化
- 刷新 context ,核心十二个方法