Spring Bean的生命周期-实例化ApplicationContext(一)

Spring Bean的生命周期往深入去看,不是简单的几句话能讲完,早就想写相关的内容,但一直觉得工作量不会低,就没有动笔写。拆成几篇来写,这样也不好给自己太大的压力。

Spring Bean的生命周期-实例化ApplicationContext(一)
image.png

主要分析,Spring容器的初始化过程,然后如何获取Bean,到最终销毁Bean整个过程经历了什么东西。

代码

主要为了分析,更多的是深入源代码中,代码写的就简单一些

//SpringApp.java文件
public class SpringApp {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("app.xml");
        HelloWorld hello = (HelloWorld) applicationContext.getBean("hello");
        hello.sayHello();
        applicationContext.close();
    }
}

//HelloWorld 文件
public class HelloWorld {
    public void sayHello(){
        System.out.println("hello World");
    }
}

//app.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="hello" class="me.aihe.HelloWorld">

    </bean>
</beans>

文件结构就是这个简单,如下

Spring Bean的生命周期-实例化ApplicationContext(一)
image.png

过程分析

  1. 新建ClassPathXmlApplicationContext实例的时候,最终实例化的构造方法如下
  • configLocations配置文件位置,支持字符串数组
  • refresh是否自动的刷新上下文,否则在配置之后要手动的调用refresh方法
  • parent 父级的上下文

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }
        

其最终的实例化的父类为:

   //创建AbstractApplicationContext
    public AbstractApplicationContext() {
        this.resourcePatternResolver = getResourcePatternResolver();
    }

    public AbstractApplicationContext(ApplicationContext parent) {
        this();
        setParent(parent);
    }
    
    protected ResourcePatternResolver getResourcePatternResolver() {
        return new PathMatchingResourcePatternResolver(this);
    }
  1. 设置配置文件的位置, setConfigLocation(String location)
   //支持可变参数,如果locations不存在,那么默认为null
    public void setConfigLocations(String... locations) {
        if (locations != null) {
            Assert.noNullElements(locations, "Config locations must not be null");
            this.configLocations = new String[locations.length];
            //处理配置文件的位置,稍作处理
            for (int i = 0; i < locations.length; i++) {
                this.configLocations[i] = resolvePath(locations[i]).trim();
            }
        }
        else {
            this.configLocations = null;
        }
    }

  1. 刷新Context, 调用refresh()方法,这里先贴出来整个方法, Refreash方法是容器中非常重要的一个方法,一时间这里不好完全讲说明白,先把每个方法的用途注释写上,接下来我们再一起看看其内部是怎么回事
public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 准备刷新的Context.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            // 告诉子类刷新内部的Bean Factory
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            // 准备好Context使用的Bean Factory
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                // 设置Bean的 postPorcess
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                // 调用BeanFactory的 postProcess
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                // 注册Bean的PostProcess
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                //对上下文中的消息源进行初始化
                initMessageSource();

                // Initialize event multicaster for this context.
                //初始化上下文的事件机制
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                //初始化其它特殊的beans
                onRefresh();

                // Check for listener beans and register them.
                //检查这些Bean,并且向容器注册
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                //实例化所有的非懒加载的 Singleton
                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.
                // 销毁已经创建的singletons避免对资源产生不好的影响
                destroyBeans();

                // Reset 'active' flag.
                //重置 active 标志
                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...
                //重置所有的caches
                resetCommonCaches();
            }
        }
    }
涉及到类
  • PathMatchingResourcePatternResolver
  • StandardEnvironment
TODO
  • AbstractRefreshableConfigApplicationContext.resolvePath,其中涉及到了Environment.resolveRequiredPlaceholders(String path)

最后

读代码,如果条理清晰,是一件比较爽的工作,尽量不要急功近利,我们一步一步来,压力都会比较小一点。

参考

上一篇:Spring(08)——方法注入之替换方法实现


下一篇:微信小程序开发系列四:微信小程序之控制器的初始化逻辑