SpringIOC初始化过程源码跟踪及学习

Spring版本 4.3.2,ssm框架

代码过宽,可以shift + 鼠标滚轮 左右滑动查看

web.xml



    webAppRootKey
    web.root


    contextConfigLocation
    classpath:spring/applicationContext.xml



    org.springframework.web.util.WebAppRootListener


    org.springframework.web.context.ContextLoaderListener

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.
* 

This implementation expects custom contexts to implement the * {@link ConfigurableWebApplicationContext} interface. * Can be overridden in subclasses. *

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.
* 

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. *

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 refreshed.
* 

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. *

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>> initializerClasses = determineContextInitializerClasses(sc); // 没有自定义操作,所以跳过。有则实例化 Initializer for (Class> 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 initializer : this.contextInitializers) { // contextInitializers 对 context 进行初始化 initializer.initialize(wac); } }

3.1.1 determineContextInitializerClasses

跟踪标记3.1.1的方法。

此方法在ContextLoader类中实现。

// 3.1.1web.xml中没有定义,所以此处返回空list
List>> 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>>
            determineContextInitializerClasses(ServletContext servletContext) {

        List>> classes =
                new ArrayList>>();


        //是否有自定义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.
* 

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 ,核心十二个方法

你可能感兴趣的:(SpringIOC初始化过程源码跟踪及学习)