Spring IOC的创建原理

前言

先来回顾一下创建Spring IOC容器的两种方式:

  1. 通过BeanFactory创建;
  2. 通过ApplicationContext创建;

其中ApplicationContext又可以分为XML和Annotation两种形式,Annotation形式以AnnotationConfigApplicationContext子类为代表。

BeanFactory继承结构

BeanFactory继承类图
  • AutowireCapableBeanFactory接口定义IOC容器的自动装配功能,在ApplicationContext类中可以通过getAutowireCapableBeanFactory()获取该接口,从而使得ApplicationContext拥有自动装配功能;
  • Hierarchical的意思是"分级",所以HierarchicalBeanFactory允许应用启动多个BeanFactory并设置父子关系。例如Spring+SpringMVC应用中的父子容器;
  • ListableBeanFactory中定义获取多个Bean的方法,而BeanFactory中只定义了获取单个Bean的方法。
  • ApplicationContext继承了BeanFactory,同时拥有了AutowireCapableBeanFactoryHierarchicalBeanFactoryListableBeanFactory三个接口定义的功能。
  • ConfigurableBeanFactory接口定义了ioc容器的可定制性,它定义了设置类装载器,属性编辑器,容器初始化后置处理器等方法;几乎所有的beanFactory都会实现这个接口,赋予了BeanFactory可扩展的功能。

ApplicationContext继承结构

ApplicationContext继承类图
  • ApplicationContext分为web环境类和非web环境类,如果要在WEB容器中创建IOC需要使用WebApplicationContext的实现类。
  • FileSystemXmlApplicationContextClassPathXmlApplicationContext类都是通过XML文件创建IOC容器,不同的是要加载的文件位置不同,前者位于文件系统中,后者位于classpath当中。
  • AnnotationConfigApplicationContext是基于注解的形式来创建IOC容器,采用java配置文件。

我们将通过Spring(具体版本为:4.3.9.RELEASE)注解的方式来分析IOC容器的创建过程,主要通过AnnotationConfigApplicationContext类。入口程序如下

@Test
public void test(){
    AnnotationConfigApplicationContext applicationContext = new
          AnnotationConfigApplicationContext(MainConfig.class);
    System.out.println("IOC容器创建完毕");
    Object person = applicationContext.getBean("person");
    Person person1 = applicationContext.getBean(Person.class);
    System.out.println(person == person1);
}

其中MainConfig是Spring的注解配置类,其代码如下

@Configuration
public class MainConfig {

    /**
     * @Bean注解的value属性用来定义注册到IOC容器中的bean name
     * 如果不指定value属性,则用方法名作为bean name
     * @return
     */
    @Bean(value = "person")
    public Person getPerson(){
        Person person = new Person();
        person.setName("Jerry");
        person.setAge(18);
        person.setNickName("J");
        return person;
    }
}

AnnotationConfigApplicationContext的继承体系

AnnotationConfigApplicationContext继承体系图

由上图可知,AnnotationConfigApplicationContextApplicationContext接口某个具体实现类,而ApplicationContextBeanFactory的子接口。所以我们可以将AnnotationConfigApplicationContext看成是一种BeanFactory,他们的本质都是一样的。

ApplicationContext和BeanFactory都是用于加载Bean的,这两个类都可以看成是Spring IOC容器,但是ApplicationContext比BeanFactory提供了更多的扩展功能。

常见的ApplicationContext


上图中用红框框起来的是一些常见的ApplicationContext,它们用于各种不同的场景。在这些类实例化的过程中,都会直接或间接的调用AbstractApplicationContext.refresh()方法。而AbstractApplicationContext.refresh()方法的执行过程就是IOC容器的创建过程。下面来分析一下refresh()方法的执行过程。

refresh()执行流程

refresh()方法很重要,它是各种IOC容器初始化的入口。首先需要知道的是refresh()方法运用了“模板方法”的设计模式,后面会出个专栏分析Spring中运用的设计模式。先来看看refresh()的源码

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 1. 准备上下文信息
        prepareRefresh();
        // Tell the subclass to refresh the internal bean factory.
        // 2. 初始化BeanFactory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 3. 对BeanFactory进行功能填充
        prepareBeanFactory(beanFactory);

        try {
            // 4. 子类覆盖的方法,用于做特殊的处理,这是个钩子方法
            postProcessBeanFactory(beanFactory);
            // 5. 激活各种BeanFactoryPostProcessor处理器
            invokeBeanFactoryPostProcessors(beanFactory);
            // 6. 注册各种Bean的后置处理器,后置处理器的调用发生在getBean
            registerBeanPostProcessors(beanFactory);
            // 7. 国际化处理
            initMessageSource();
            // 8. 在容器中初始化消息广播器
            initApplicationEventMulticaster();
            // 9. 子类覆盖的方法,各⼦类来初始化其他的bean,钩子方法
            onRefresh();
            // 10. 注册Listener到消息广播器中
            registerListeners();
            // 11. 初始化剩余的所有非懒加载的单实例bean
            finishBeanFactoryInitialization(beanFactory);
            // 12. 发送ContextRefreshEvent广播,完成刷新过程
            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...
            // 13. 清除缓存
            resetCommonCaches();
        }
    }
}

refresh()正常执行完毕,即表明创建好IOC容器。分析源码可以知道refresh()方法做了13件事。

  1. 准备上下文信息;
  2. 创建BeanFactory;
  3. 对BeanFactory进行功能填充;
  4. 子类覆盖的方法,用于做特殊的处理,这是个钩子方法;
  5. 激活各种BeanFactoryPostProcessor处理器;
  6. 注册各种Bean的后置处理器,后置处理器的调用发生在getBean;
  7. 国际化处理;
  8. 在容器中初始化消息广播器;
  9. 子类覆盖的方法,各⼦类来初始化其他的bean,钩子方法;
  10. 注册Listener到消息广播器中;
  11. 初始化剩余的所有非懒加载的单实例bean;
  12. 发送ContextRefreshEvent广播,完成刷新过程;
  13. 清除缓存。

1. 准备上下文信息

protected void prepareRefresh() {
    this.startupDate = System.currentTimeMillis();
    this.closed.set(false);
    this.active.set(true);

    if (logger.isInfoEnabled()) {
        logger.info("Refreshing " + this);
    }

    // 初始化property资源,扩展点,留给子类实现
    initPropertySources();

    // 验证需要的配置文件是否加载到环境中
    // see ConfigurablePropertyResolver#setRequiredProperties
    getEnvironment().validateRequiredProperties();

    // Allow for the collection of early ApplicationEvents,
    // to be published once the multicaster is available...
    this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}

2. 创建BeanFactory

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (logger.isDebugEnabled()) {
        logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    }
    return beanFactory;
}

这个方法执行完毕之后,ApplicationContext就拥有了BeanFactory的功能。这段代码做了两件事:2.1 初始化BeanFactory;2.2 获取BeanFactory。

2.1 初始化BeanFactory

refreshBeanFactory();用来初始化BeanFactory,它最终调用的是GenericApplicationContext.refreshBeanFactory()方法,代码如下:

protected final void refreshBeanFactory() throws IllegalStateException {
    if (!this.refreshed.compareAndSet(false, true)) {
        throw new IllegalStateException(
                "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
    }
    this.beanFactory.setSerializationId(getId());
}

主要是通过getId()方法给BeanFactory设置serializationId的属性值,此处设置的值为:"org.springframework.context.annotation.AnnotationConfigApplicationContext@6cc7b4de"

2.2 获取BeanFactory
public final ConfigurableListableBeanFactory getBeanFactory() {
    return this.beanFactory;
}

此处返回的是DefaultListableBeanFactory

3. 对BeanFactory进行功能填充

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // Tell the internal bean factory to use the context's class loader etc.
    beanFactory.setBeanClassLoader(getClassLoader());
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

    // Configure the bean factory with context callbacks.
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

    // BeanFactory interface not registered as resolvable type in a plain factory.
    // MessageSource registered (and found for autowiring) as a bean.
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);

    // Register early post-processor for detecting inner beans as ApplicationListeners.
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

    // 增加对AspectJ的支持
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        // Set a temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }

    // 添加默认的系统环境Bean
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
}

此方法是对BeanFactory进行一些设置。关于这一块的解说可以参考容器的功能扩展(二)功能扩展

4. postProcessBeanFactory

postProcessBeanFactory方法是一个扩展点,在AbstractApplicationContext中是一个空方法,它主要是让子类可以重写以实现它们自己的特殊逻辑。而AnnotationConfigApplicationContext并没有对该类进行重写。

5.激活各种BeanFactoryPostProcessor处理器

BeanFactoryPostProcessorBeanPostProcessor都是Spring提供的扩展点,可以通过它们对bean的定义修改。

 protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
     PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

     // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
     // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
     if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
         beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
         beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
     }
 }

激活BeanFactoryPostProcessor的工作会委托给PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()进行处理。

6. 注册各种Bean的后置处理器

Bean的后置处理器的方法也是委托给PostProcessorRegistrationDelegate处理,具体方法为:registerBeanPostProcessors()。这⾥只是注册,真正的调⽤是在getBean的时候

待续

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

推荐阅读更多精彩内容

  • Spring容器高层视图 Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相...
    Theriseof阅读 2,812评论 1 24
  • 1.1 spring IoC容器和beans的简介 Spring 框架的最核心基础的功能是IoC(控制反转)容器,...
    simoscode阅读 6,713评论 2 22
  • 一.什么是Ioc /DI ioc容器:主要是完成了对象的创建和依赖的管理注入 二.Spring IoC体系结构 (...
    红牛蜀黍阅读 2,143评论 0 7
  • 这几个月,一直害眼疾,前前后后做了三次手术,可是老是顽固性的复发,这不,没有办法,只好再去医院,做检查以前,例行验...
    健的XUYU阅读 205评论 1 1
  • 乐观组6号 姓名:李科龙 担雪老师的话拯救了我 感悟:良师益友要多交,因为你还活着,活着就有烦恼,人必须朝前看,坚...
    李科龙阅读 1,042评论 0 0