Spring IoC分析

1 spring核心IoC

spring源码版本 version 4.0.5

1.1 BeanFactory 和 ApplicationContext

spring IoC容器的核心的表现形式就是 BeanFactory和ApplicationContext

1.1.1 BeanFactory

  1. 可以通过getBean方法获得指定的bean
  2. isSingleton 判断指定的bean是不是单例的

主要是BeanFactory的接口中方法

其中有两个BeanFactory的实现比较需要注意 DefaultListableBeanFactoryAbstractAutowireCapableBeanFactory。 使用比较多。

1.1.2 ApplicationContext

其实使用编程的方式可以创建IoC,即创建factory ,然后对factory进行各种设置再使用(参见本文1.2.2最后的示例)。但是spring提供了更加方便的ApplicationContext,同时还更加了一些额外的操作。

  1. 支持不同的信息资源。ApplicationContext扩展了MessageSource接口
  2. 访问资源。继承自DefaultResourceLoader
  3. 支持应用事件。继承自接口ApplicationEventPublisher
  4. 附件服务

1.2 IoC 容器初始化过程

第一步:Resource定位过程。通过ResourceLoader的统一接口Resource找到BeanDefinition的资源定位。

第二步:BeanDefinition 载入。把用户定义好的Bean转换成IoC容器内部的数据结构BeanDefinition。

第三步:向IoC容器注册这些BeanDefinition的过程。调用BeanDefinitionRegistry.registerBeanDefinition实现。这个注册过程把载入过程中解析的BeanDefinition向IoC注册。实际上IoC内部是将BeanDefinition注入好一个HashMap中,通过这个HashMap来持有这些BeabDefinition的数据。

这个过程中不包含依赖注入,依赖注入和bean定义(BeanDefinition)的载入是两个独立的过程。 依赖注入一般发生在第一次使用getBean获取bean的时候,但是如果bean配置了lazyinit的话,会提前初始化,不用等到第一次getBean触发

1.2.1 BeanDefinition的资源定位

看BeanDefinition的资源定位,以FileSystemXmlApplicationContext为例,查看一下过程。
FileSystemXmlApplicationContext的构造方法中,refresh() 方法启动IoC容器。 几种ApplicationContext的子类都是在构造方法中refresh里面启动容器的。

方法调用链路:

AbstractApplicationContext.refresh()-->obtainFreshBeanFactory()-->refreshBeanFactory-->AbstractRefreshableApplicationContext.refreshBeanFactory()-->loadBeanDefinitions-->AbstractXmlApplicationContext.loadBeanDefinitions()

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    // 通过config获取
    Resource[] configResources = getConfigResources();
    if (configResources != null) {
        reader.loadBeanDefinitions(configResources);
    }
    // 通过字符串获取
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
        reader.loadBeanDefinitions(configLocations);
    }
}

如果得到是String[] configLocations 则会最后进入到 AbstractBeanDefinitionReader的public int loadBeanDefinitions(String location, Set<Resource> actualResources)。此方法内部把String 转换成Resource。 整个获取BeanDefinition的过程就结束了,定位BeanDefinition的主要目的就是把资源位置转换成可以处理的Resource

上面两个分支最后都是走如下路径去加载BeanDefinition

AbsractBeanDefinitionReader.loadBeanDefinitions(Resource... resources) --> XmlBeanDefinitionReader.loadBeanDefinitions(Resource resource) (loadBeanDefinitions(EncodedResource encodedResource))

1.2.2 BeanDefinition的载入

在找到资源位子时候,获取到很多的Resources只有,在需要根据资源类型,选择不同类型的BeanDefinitionReader载入资源,例如Xml的配置文件,最后载入的时候就是XmlBeanDefinitionReader来执行载入。

之前的说到过,ApplicationContext的子类都是在构造方法里面通过refresh方法启动容器的。所有BeanDefinition的资源定位和载入BeanDefinition的入口都是在refresh方法中。

核心入口:

@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            // refresh前准备工作
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            // 通知子类调用内部 refresh bean factory的方法 refreshBeanFactory(),
            // 此refreshBeanFactory()是资源定位和载入的入口
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            // 设置beanFactory的标准上下文特征
            prepareBeanFactory(beanFactory);

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

                // Invoke factory processors registered as beans in the context.
                // 调用beanFactory的后处理器
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                // 注册bean 后处理器,在bean创建的过程中调用
                registerBeanPostProcessors(beanFactory);

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

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

                // Initialize other special beans in specific context subclasses.
                // 利用子类初始化一些特殊bean
                onRefresh();

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

                // Instantiate all remaining (non-lazy-init) singletons.
                // 实例化所有(non-lazy-init)的单例组件
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                // 最后一步,发布容器,结束refresh过程
                finishRefresh();
            }

            catch (BeansException ex) {
                // Destroy already created singletons to avoid dangling resources.
                // 防止单例组件占用资源,在异常中销毁
                destroyBeans();

                // Reset 'active' flag.
                // 设置'active'标识
                cancelRefresh(ex);

                // Propagate exception to caller.
                // 向外层调用这抛出异常
                throw ex;
            }
        }
    }

前面步骤和查找resource一样,xml形式的配置文件,最后走到XmlBeanDefinitionReader中(定位资源中有提到,最后进入XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource)),进入如下方法:

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }

        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<EncodedResource>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        try {
            // 将资源转换成输入流
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                // 在doLoadBeanDefinitions方法中实现BeanDefinition的载入
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                inputStream.close();
            }
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }

从输入流中读取bean的定义,从doLoadBeanDefinitions 方法进入,开始载入BeanDefinition。在doLoadBeanDefinitions方法中通过InputSource和recource得到Document,紧接着调用registerBeanDefinitions,实际调用到DefaultBeanDefinitionDocumentReader.registerBeanDefinitions-->doRegisterBeanDefinitions(root)

protected void doRegisterBeanDefinitions {
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        if (StringUtils.hasText(profileSpec)) {
            Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                    profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            if (!this.environment.acceptsProfiles(specifiedProfiles)) {
                return;
            }
        }
        
        // 任何嵌套的 <beans> 元素将会导致这个方法的循环,
        // 为了正确的传播和保存<beans>的default-*属性,追踪当前代理(可能为null)。
        // 创建一个新的代理对象,指向原来对象来达到回滚的目的,最终重置this.delegate到原来的对象。
        // 整个的表现模拟了一个代理栈,而不需要一个代理
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(this.readerContext, root, parent);

        preProcessXml(root);
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }

再进入parseBeanDefinitions解析。在parseBeanDefinitions中,如果是默认的namespace就进入parseDefaultElement(ele, delegate),如果不是就进入delegate.parseCustomElement(ele),在parseDefaultElement中分别解析 importaliasbeanbeans ,其中beans会进入递归,调用doRegisterBeanDefinitions。bean是节点才是我创建bean的解析过程。其中bean分支左后进入到DefaultBeanDefinitionDocumentReader.processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 在BeanDefinitionParserDelegate中完成bean的解析,结果返回到BeanDefinitionHolder中
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        // BeanDefinitionParserDelegate在对holder进行修饰(解析自定义的属性)
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // Register the final decorated instance.
            // 向Ioc真正注册装饰之后的BeanDefinition实例
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        }
        catch (BeanDefinitionStoreException ex) {
            getReaderContext().error("Failed to register bean definition with name '" +
                    bdHolder.getBeanName() + "'", ele, ex);
        }
        // Send registration event.
        // BeanDefinition注册完成之后发送消息
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

1.2.3 BeanDefinition在IoC容器的注册

bean的注册过程,从上面解析过程完成之后,就开始注册了,上面带出显示

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

进入BeanDefinitionReaderUtils,registerBeanDefinition方法中最后调用BeanDefinitionRegistry接口的registerBeanDefinition。查看DefaultListableBeanFactory中的实现

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");

        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                // 校验BeanDefinition
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }
        
        // private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
        // key是beanName,value是BeanDefinition
        synchronized (this.beanDefinitionMap) {
            BeanDefinition oldBeanDefinition = this.beanDefinitionMap.get(beanName);
            if (oldBeanDefinition != null) {
                // 如果同名的bean已经注册,但是设置allowBeanDefinitionOverriding为false(即不允许定义重复),则抛出异常
                // 默认的allowBeanDefinitionOverriding为true
                // 网上看到的设置allowBeanDefinitionOverriding的方法,但此方法有点局限在web项目中,方案链接如下:
                // http://blog.csdn.net/zgmzyr/article/details/39380477
                // 那么还有没有更好的办法呢 ?
                if (!this.allowBeanDefinitionOverriding) {
                    throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                            "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                            "': There is already [" + oldBeanDefinition + "] bound.");
                }
                // 如果设置允许不定义不一致,但是重复的情况下,判断BeanDefition的role级别,日志提醒
                else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                    // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                    if (this.logger.isWarnEnabled()) {
                        this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                                " with a framework-generated bean definition ': replacing [" +
                                oldBeanDefinition + "] with [" + beanDefinition + "]");
                    }
                }
                else {
                    if (this.logger.isInfoEnabled()) {
                        this.logger.info("Overriding bean definition for bean '" + beanName +
                                "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
                    }
                }
            }
            else {
                // 如果没有重复,添加beanName到list中,按照注册顺序排序
                this.beanDefinitionNames.add(beanName);
                this.frozenBeanDefinitionNames = null;
            }
            // 添加BeanDefinition到map中
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }

        resetBeanDefinition(beanName);
    }

回答上面提到的问题,如果设置allowBeanDefinitionOverriding还有什么方法,那就是使用IoC的释放,方法换掉

@Test
public void testProgramWayIoC() {
    // 确定资源
    Resource res = new ClassPathResource("application.xml");
    // 创建一个BeanFactory,设置部分不想使用默认值的属性
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    factory.setAllowBeanDefinitionOverriding(false);
    // 创建BeanDefinitionReader
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
    // 加载DeanDefinition并向IoC容器注册
    reader.loadBeanDefinitions(res);
    // 使用bean
    TestBean test = factory.getBean("testBean",TestBean.class);
    test.testMethod();

}

虽然这样可以,但是相比较来讲,代码注释的地方提到的方案,相对更加实用。这里说明这个只是说可以用编程的方式使用IoC容器。

1.3 IoC的依赖注入

依赖注入的地方是在第一次使用getBean的时候注入的。但是当设置了lazy-init的时候会在BeadDefinition载入的时候注入。

getBean是BeanFactory接口提供的方法,其实现在AbstractBeanFactory中

另外:ListableBeanFactory中有一个<T> T getBean(Class<T> requiredType) throws BeansException;的接口,在DefaultListableBeanFactory中实现。

依赖注入过程

AbstractBeanFactory.doCreateBean()-->AbstractBeanFactory.createBean()-->AbstractAutowireCapableBeanFactory.createBean()-->AbstractAutowireCapableBeanFactory.doCreateBean()-->AbstractAutowireCapableBeanFactory.createBeanInstance()-->initializeBean()-->registerDisposableBeanIfNecessary

1.4 前后处理器与容器的感知

1.4.1 BeanPostProcessor 与 BeanFactoryPostProcessor

BeanFactoryPostProcessor 是BeanFactory的后置处理器

public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

他只在beanFactory创建完成时候调用过一次,可以得到一个BeanFactory的子类。操作BeanFactory

BeanPostProcessor 是Bean的前后置处理器,可以在bean的初始化前后操作。可是获得beanName,修改Bean

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

这里面的两个方法在所有的bean的初始化过程中都会调用,可以理解为一个拦截去的样子。

InitializingBean 接口,主要实在bean实例化之后设置属性,即各种感知器执行完成之后

1.4.2 感知(Aware)

有些时候需要在bean使用IoC容器,对IoC容器进行操作,这个时候就可以利用感知器的特点得到IoC进行操作。

几种常用的感知器:

  1. BeanNameAware, 可以在Bean中得到它在IoC容器中的实例名称
  2. BeanFactoryAware, 可以在Bean中得到bean所在的容器(beanFactory对象),从而在bean中直接使用IoC容器的服务。
  3. ApplicationContextAware, 可以在bean中得到bean所在应用的上下文,从而直接在Bean中使用上下文的服务。例如利用上下文getBean
  4. MessageSourceAware, 在Bean中得到消息源
  5. ApplicationEventPublisherAware,在Bean中得到上下文事件发布器,从而可以在Bean中发布应用上下文事件
  6. ResourceLoaderAware, 在Bean中得到ResourceLoader,从而在bean中使用ResourceLoader加载外部对应的Resource资源

1.5 Bean生命周期

bean的生命周期,整个过程就是先执行BeanFactory的创建,如果有就不用再创建,在执行beanFactory的后置处理器,再执行实例化感知器。接下实例化。注入属性。调用各种Bean的感知器。调用bean的前后置处理器。如下图:

图片来源:http://www.cnblogs.com/zrtqsk/p/3735273.html

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

推荐阅读更多精彩内容