美文网首页
SpringIOC初始化过程源码跟踪及学习

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

作者: 一颗老鼠屎 | 来源:发表于2018-10-22 11:19 被阅读0次

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

    相关文章

      网友评论

          本文标题:SpringIOC初始化过程源码跟踪及学习

          本文链接:https://www.haomeiwen.com/subject/oyiwzftx.html