Spring 框架学习(三):IoC 容器

Spring 框架学习(三):IoC 容器

概述

IoC 容器的核心是依赖反转模式。许多应用都是由两个或多个类通过彼此的合作来实现业务逻辑的,这是的每个对象都需要与其合作对象的引用。如果这个获取过程要靠自己实现,那将导致代码高度耦合并且难以测试。在 Spring 中通过把依赖对象的获取交给 IoC 容器来完成,在解耦代码的同时提高了代码的可测试性。

对依赖关系的统一管理,在一定程度上也降低了面向对象系统的复杂性。

在 Spring IoC 容器设计中,有两个主要的容器系列:一个是实现 BeanFactory 接口的简单容器系列,只实现了容器最基本的功能;另一个是 ApplicationContext 应用上下文,它作为容器的高级形态,增加了许多面向框架的特性,同时对应用环境作了许多适配。

BeanFactory 容器

XmlBeanFactory 类图.png
  1. BeanFactory 定义了容器最基本的接口:获取对象 getBean、判断是否包含 containsBean等。
  2. HierarchicalBeanFactory 增加了双亲 IoC 容器的管理功能:getParentBeanFactory 接口。
  3. ConfigurableBeanFactory 增加了设置双亲 IoC 容器的接口:setParentBeanFactory,Bean 后置处理器的添加:addBeanPostProcessor 等等。
  4. DefaultListableBeanFactory 是一个基本 IoC 容器的实现。
  5. XmlBeanFactory 相比 DefaultListableBeanFactory 增加了对 xml 配置文件的支持。

ApplicationContext 容器

FileSystemXmlApplicationContext 类图.png
  1. ApplicationContext 同样继承了 HierarchicalBeanFactory、BeanFactory 接口。
  2. ConfigurableWebApplication 也继承了 ConfigurableBeanFactory 接口。
  3. ApplicationContext 通过继承 MessageSource、ApplicationEventPuhlisher、ResourceLoader 接口,增加了许多高级特性。
  4. FileSystemXmlApplicationContext 是一个完整的容器实现,从文件系统读取 xml 配置来初始化容器。

容器初始化过程

我们以 FileSystemXmlApplicationContext 为例来查看容器初始化过程,其入口是构造方法:

    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {
        // 设置双亲容器               
        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            // 读取配置,初始化容器
            refresh();
        }
    }

refresh 方法负责初始化容器的一系列操作,具体有哪些操作看该方法的大纲即可知道:

    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 做准备操作,加载 placeholder 里配置的环境变量,校验所需环境变量是否全部存在
            prepareRefresh();

            // 创建 BeanFactory,解析 xml 配置,将 bean 定义加载到 map 中
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // 一些准备工作,比如设置 ClassLoader、StandardBeanExpressionResolver、BeanPostProcessor
            prepareBeanFactory(beanFactory);

            try {
                // 又设置了一些 BeanPostProcessor
                postProcessBeanFactory(beanFactory);

                // 调用 BeanFactory 后处理器
                invokeBeanFactoryPostProcessors(beanFactory);

                // 注册 Bean 的后处理器,前面是 BeanFactory 后处理器,这里是 Bean 的
                registerBeanPostProcessors(beanFactory);

                // 初始化消息源
                initMessageSource();

                // 初始化事件机制
                initApplicationEventMulticaster();

                // 初始化其他特殊的 bean
                onRefresh();

                // 注册事件 Listener
                registerListeners();

                // 初始化所有单例 Bean,非单例 Bean 是在 getBean 的时候初始化的
                finishBeanFactoryInitialization(beanFactory);

                // 发布容器初始化完成事件
                finishRefresh();
            } catch (BeansException ex) {
                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }
        }
    }

比较重要的是以下两步:

  1. 加载配置,创建 Bean 工厂:obtainFreshBeanFactory。
  2. 初始化所以单例 Bean:finishBeanFactoryInitialization。

obtainFreshBeanFactory

obtainFreshBeanFactory 加载配置,创建 BeanFactory 的调用顺序如下图所示:

obtainFreshBeanFactory 时序图.png

最终调用 BeanDefinitionParserDelegate 类的 parseBeanDefinitionElement 解析 bean 的配置。同时也会调用 parseCustomElement 解析其他配置,该方法最终会调用 NamespaceHandlerSupport 里注册的 BeanDefinitionParser 来解析这些特殊的配置。

public class ContextNamespaceHandler extends NamespaceHandlerSupport {

    public void init() {
        registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
        registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
        registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
        registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
        registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
        registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
        registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
    }
}

finishBeanFactoryInitialization

finishBeanFactoryInitialization 这一步会初始化所有单例的 Bean,非单例的 Bean 会在用户调用 getBean 方法的时候被初始化,单例的则是由 Spring 框架来调用 getBean 方法进行实例化。

    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        // Initialize conversion service for this context.
        if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
                beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
            beanFactory.setConversionService(
                    beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
        }

        // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
        String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
        for (String weaverAwareName : weaverAwareNames) {
            getBean(weaverAwareName);
        }

        // Stop using the temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(null);

        // Allow for caching all bean definition metadata, not expecting further changes.
        beanFactory.freezeConfiguration();

        // Instantiate all remaining (non-lazy-init) singletons.
        beanFactory.preInstantiateSingletons();
    }
    // in DefaultListableBeanFactory.java
    public void preInstantiateSingletons() throws BeansException {
        if (this.logger.isInfoEnabled()) {
            this.logger.info("Pre-instantiating singletons in " + this);
        }
        List<String> beanNames;
        synchronized (this.beanDefinitionMap) {
            // Iterate over a copy to allow for init methods which in turn register new bean definitions.
            // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
            beanNames = new ArrayList<String>(this.beanDefinitionNames);
        }
        for (String beanName : beanNames) {
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                if (isFactoryBean(beanName)) {
                    final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                            public Boolean run() {
                                return ((SmartFactoryBean<?>) factory).isEagerInit();
                            }
                        }, getAccessControlContext());
                    }
                    else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        // 调用 getBean 方法触发 Bean 的创建
                        getBean(beanName);
                    }
                }
                else {
                    getBean(beanName);
                }
            }
        }
    }   

getBean

getBean 方法调用了 createBean,时序图如下:

createBean 时序图.png

主要分为以下几步:

  1. 创建 Bean 实例:createBeanInstance。
  2. 填充配置的属性值:populateBean。
  3. 调用初始化方法:initializeBean。
  4. 注册 Bean 的销毁方法:registerDisposableBeanIfNecessary。

总结

Spring IoC 容器主要负责管理 Bean 的生命周期,Bean 都是放在 BeanFactory 工厂类中管理的,Bean 的配置以 xml 为主。那么初始化的过程,主要就是:

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

推荐阅读更多精彩内容