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代称)调用。该类继承关系如下:

ContextLoaderListener继承图.png

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的继承关系:

XmlWebApplicationContext继承图.png

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 ,核心十二个方法
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容