Spring IoC源码分析(二)

一、上文回顾

上节基于可达性分析讲了Spring的BeanFactory的初始化,现在来回顾一下BeanFactory创建过程中关键的方法refresh,另外已经从当前文章开始后续的文章会把源码更新到spring5.x版本,这对于之前的文章不会涉及很大的影响。

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();
            }
        }
    }

上节讲到了这一句:

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

再进入obtainFreshBeanFactory方法中的关键步骤refreshBeanFactory方法

protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

可以发现其实代码很简单,找到上面的createBeanFactory()方法,它创建了ConfigurableListableBeanFactory,到此BeanFactory就正式创建了

然后再看一下后面的第二句

customizeBeanFactory(beanFactory)

翻译一下,定制BeanFactory,这啥意思啊,我的BeanFactory不是创建出来了吗,还要定制?定制什么?点进去看看把……

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        if (this.allowBeanDefinitionOverriding != null) {
           beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.allowCircularReferences != null) {
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
}

哦!原来是这样,由于BeanFactory是用来存放Bean的,因此难免会碰到Bean定义重写(这应该很重要,我想增强BeanDefinition就靠这个)和Bean循环引用的情况(这有时可能会产生问题),ok,问题解决,接下来回顾及拓展结束,进入本节的正题

二、思考

现在容器已经产生了,那么,创建容器是用来解决什么问题的呢?既然是一个Bean工厂——当然是用来放Bean的咯,准确的来说——是用来放Bean的定义的。

其实这也不难理解,如果BeanFactory存放的是实例Bean,那么即代表在容器创建完成后需要将应用中定义的所有Bean都进行实例化,但实际上可能大多数Bean都不是我们立即想使用的,这会导致应用启动缓慢,且非常影响性能,另外,倘若我想在程序运行时对Bean进行一些增强,这样做也并不具有可拓展性

所以拍脑袋一想就不难发现,如果BeanFactory中存放的只是一个Bean的定义,那么上述的问题都可以得到解决,BeanFactory只需要在用户需要使用Bean的时候为其创建(当然有些Bean可能需要应用运行时即创建),且能够通过修改Bean定义对其进行增强,这里我不禁要感叹这是多棒的设计啊

这里所说的Bean定义,在Spring对应的即为BeanDefinition,在上面的refreshBeanFactory方法中,可以发现,当BeanFactory被创建之后,他会在第一时间做loadBeanDefinitions操作,即将Bean定义加载到Bean工厂中,精彩内容要开始了!

三、loadBeanDefinitions(DefaultListableBeanFactory beanFactory)

AbstractRefreshableApplicationContext下的loadBeanDefinitions是一个抽象方法,这也很好理解,一个Spring应用可以有很多定义Bean的方法,如xml、注解方式,甚至还可以有远程获取Bean的方法,因此也有很多获取Bean定义的方法,简单看一下重写了loadBeanDefinitions方法的类有哪些

img

这些类都重写了loadBeanDefinitions方法,不难通过类名判断各个类的意思,本节会先通过AbstractXmlApplicaitonContext来将如何加载bean定义,虽然现在普遍流行的是springboot通过注解形式来加载bean,但是AbstractXmlApplicaitonContext的loadBeanDefinitions方法下有很多值得关注的内容,且相信很多人在新手入门时接触的可能更多的是XML,因此也更容易理解。下面贴源码~在阅读源码之前,不难想到,如果需要从XML文件中读取配置,那么就需要通过I/O来获取文件数据流,带着这个概念看下面的代码就不难理解

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
            beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }

这个方法有趣的地方在于方法最后调用了重载的loadBeanDefinitions方法,传入了beanDefinitionReader,再看上面的一些操作,可以推断,当前的loadBeanDefinition方法实际上是对BeanDefinitionReader做了一些初始化和赋值操作,让其能在加载bean的时候能正常工作,这一部分涉及了很多很重要的概念,下面重点介绍BeanDefinitionReader的相关操作

1.创建BeanDefinitonReader,初始化Bean注册器

XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

直接点进去

/**
 * Create new XmlBeanDefinitionReader for the given bean factory.
 * @param registry the BeanFactory to load bean definitions into,
 * in the form of a BeanDefinitionRegistry
 */
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
   super(registry);
}

发现调用了XmlBeanDefinitionReader的构造器的父类构造方法,这里令我感到奇怪的是,为什么参数从ConfigurableListableBeanFactory变成了BeanDefinitionRegistry……后来仔细一看,发现ConfigurableListableBeanFactory原来实现了BeanDefinitionRegistry接口,那么BeanDefinitionRegistry接口为其提供了什么能力?看一下代码吧

public interface BeanDefinitionRegistry extends AliasRegistry {
    //注册Bean定义
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException;
    //删除Bean定义
    void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
    //获取Bean定义
    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
    //判断Bean定义是否存在
    boolean containsBeanDefinition(String beanName);
    //获取所有Bean定义的names
    String[] getBeanDefinitionNames();
    //获取Bean定义的数目
    int getBeanDefinitionCount();
    //是否使用Bean name存储的定义
    boolean isBeanNameInUse(String beanName);
}

这样BeanDefinitionRegistry接口提供的功能就一目了然了,

回到上面XmlBeanDefinitionReader的构造方法的super(registry)中,可以看到下面的代码片段

protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        this.registry = registry;

        // Determine ResourceLoader to use.
        if (this.registry instanceof ResourceLoader) {
            this.resourceLoader = (ResourceLoader) this.registry;
        }
        else {
            this.resourceLoader = new PathMatchingResourcePatternResolver();
        }

        // Inherit Environment if possible
        if (this.registry instanceof EnvironmentCapable) {
            this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
        }
        else {
            this.environment = new StandardEnvironment();
        }
}

BeanDefinitionReader通过传入的参数为自己的Bean注册器赋值,这样在读取XML文件后就有了注册Bean的工具,这比较好理解。然后是该方法中也比较重要的两个操作,初始化ResourceLoader&Environment。

首先大致了解什么是ResourceLoader和Environment。其实在两个接口的源码中可以非常清晰的看到他们的功能:

ResourceLoader:

用于加载classpath或文件系统下的资源

Strategy interface for loading resources (e.. class path or file system resources)

Environment:

代表了当前应用运行时所处环境,这也不难理解,开发流程中我们难免会在开发环境、测试环境、生产环境之间切换,不同的环境使用不同的profile和不同的properties。当然profile和properties暂时不深究,理解到这里就够了

Interface representing the environment in which the current application is running

四、loadBeanDefinitions(XmlBeanDefinitionReader reader)

第三个标题loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法中,主要讲解了以下操作

XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

由于后面的几句都比较好理解,因此不做介绍了,重点关注其调用的重载的loadBeanDefinitions方法

,也就是本标题讨论的内容,点进来看看吧~

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = this.getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }

        String[] configLocations = this.getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }
}

显而易见,loadBeanDefinitions(XmlBeanDefinitionReader reader)方法通过重载的loadBeanDefinitions(Resource... resources)loadBeanDefinitions(String... locations)方法加载了Bean定义,即从配置资源和配置路径下加载Bean定义,分别点进去发现,前者最终跳转到了BeanDefinitionReader的实现类XmlBeanDefinitionReader的以下方法:

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(resource));
}

后者则调用了AbstractBeanDefinitionReaderloadBeanDefinitions(String location, @Nullable Set<Resource> actualResources),不过这个分支很快通过解析路径转换为 Resource 以后也会进到loadBeanDefinitions(EncodedResource resource)方法中。

由于之后的方法调用链比较长,因此以方法注解的形式呈现,同时省略了不重要的代码

1.loadBeanDefinitions(EncodedResource encodedResource)

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        ……
        //这里使用了ThreadLocal来存储资源
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<>(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());
                }
                //这里是关键的步骤,看方法名知道其用来加载Bean定义
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                inputStream.close();
            }
        }
        catch (IOException ex) {
            ……
        }
}

2.doLoadBeanDefinitions(inputSource, encodedResource.getResource())

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            //将xml文件转换成了Document
            Document doc = doLoadDocument(inputSource, resource);
            //根据document执行bean的注册,重要方法,点进去看看
            return registerBeanDefinitions(doc, resource);
        }
        catch (BeanDefinitionStoreException ex) {
            ……
        }
}

3.registerBeanDefinitions(doc, resource)

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //创建DocumentReader
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        //获取之前已经注册的bean定义数量
        int countBefore = getRegistry().getBeanDefinitionCount();
        //根据document注册bean定义,重要,点进去看看
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        //返回值为新注册的bean定义数量
        return getRegistry().getBeanDefinitionCount() - countBefore;
}

4.registerBeanDefinitions(doc, createReaderContext(resource))

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        //获取xml的document的树状根节点
        Element root = doc.getDocumentElement();
        //root及子节点即为需要注册的bean定义,重要,点进去看看
        doRegisterBeanDefinitions(root);
}

5.doRegisterBeanDefinitions(root)

protected void doRegisterBeanDefinitions(Element root) {
        /* 我们看名字就知道,BeanDefinitionParserDelegate 必定是一个重要的类,它负责解析 Bean 定义
         * 这里为什么要定义一个 parent? 看到后面就知道了,是递归问题,
         * 因为 <beans /> 内部是可以定义 <beans /> 的,所以这个方法的 root 其实不一定就是 xml 的根节点,也可以是嵌套在里面的 <beans /> 节点,从源码分析的角度,我们当做根节点就好了
         */
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);

        if (this.delegate.isDefaultNamespace(root)) {
            // 这块说的是根节点 <beans ... profile="dev" /> 中的 profile 是否是当前环境需要的,
            // 如果当前环境配置的 profile 不包含此 profile,那就直接 return
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }
        preProcessXml(root);
        //这里的调用很重要,点进去看看
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
}

6.parseBeanDefinitions(root, this.delegate)

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            //从这里开始解析节点
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    if (delegate.isDefaultNamespace(ele)) {
                        // 解析 default namespace 下面的几个元素
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        // 解析其他 namespace 的元素
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
}

这里要插一句,有关与namespace这里不做过多的介绍,其实不难理解,可以去百度,这里也不影响源码的分析,直接点进parseDefaultElement(ele, delegate)看看

7.parseDefaultElement(ele, delegate);

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            //解析import标签
            importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            //解析别名标签
            processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            //解析bean标签,这是阅读的重点,点进去看看
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // 解析嵌套的bean标签
            doRegisterBeanDefinitions(ele);
        }
}

8.processBeanDefinition(ele, delegate)

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        //将解析出来的bean定义存储到一个BeanDefinitionHolder实例中,核心方法,BeanDefinition正是在这个方法中被创建,点进去看到本文标题8-1
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            //decorate,修饰的意思,可以简单看一眼源码,是对bean定义加了target和一些interceptor,由于这章重点关注的是bean定义如何被注册到工厂中,因此该步骤以后在进行详述
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // 注册最终被增强过的bean定义,点进去,看本文标题8-2
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
}

8-1.parseBeanDefinitionElement(ele)

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        //获取bean id
        String id = ele.getAttribute(ID_ATTRIBUTE);
        //获取bean name
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
        //获取bean 别名集合
        List<String> aliases = new ArrayList<>();
        //处理bean name字符串
        if (StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
        }
        //若bean id为空,则从别名中取出第一个作为bean name
        String beanName = id;
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            beanName = aliases.remove(0);
            ……
        }
        //校验beanName唯一性
        if (containingBean == null) {
            checkNameUniqueness(beanName, aliases, ele);
        }
        //这里BeanDefinition终于出现了!
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            // 如果都没有设置 id 和 name,那么此时的 beanName 就会为 null,进入下面这块代码产生
            if (!StringUtils.hasText(beanName)) {
                try {
                    //传参进来时containingBean为null,所以不讨论
                    if (containingBean != null) {
                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    //这里是对beanName和aliases做处理,也不是很重要,简单理解一下就行
                    else {
                        beanName = this.readerContext.generateBeanName(beanDefinition);
                        String beanClassName = beanDefinition.getBeanClassName();
                        if (beanClassName != null &&
                                beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                                !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                            aliases.add(beanClassName);
                        }
                    }
                    ……
                }
                catch (Exception ex) {
                    ……
                }
            }
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
}

8-2.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())

public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // 获取bean name
        String beanName = definitionHolder.getBeanName();
        //将bean definition注册到registry中,之前提到过ConfigurableListableBeanFactory实现了BeanRegistry接口,因此即为将bean definition注册到了bean工厂中
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // 注册bean的别名
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
}

9.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition())

再坚持一下,马上就要完成了!这串代码很长,和我一起深呼吸放松一下

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

        ……

        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                ……
            }
        }

        BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        //处理重复名称的 Bean 定义的情况
        if (existingDefinition != null) {
            if (!isAllowBeanDefinitionOverriding()) {
                //若不允许覆盖,这里会抛出异常
                ……
            }
            else if (existingDefinition.getRole() < beanDefinition.getRole()) {
                //log...用框架定义的 Bean 覆盖用户自定义的 Bean
                if (logger.isWarnEnabled()) {
                    ……
                }
            }
            // log...用新的 Bean 覆盖旧的 Bean
            else if (!beanDefinition.equals(existingDefinition)) {
                if (logger.isInfoEnabled()) {
                    ……
                }
            }
            // log...用同等的 Bean 覆盖旧的 Bean,这里指的是 equals 方法返回 true 的 Bean
            else {
                if (logger.isDebugEnabled()) {
                    ……
                }
            }
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        //不存在重名bean
        else {
            // 判断是否已经有其他的 Bean 开始初始化了.
            // 注意,"注册Bean" 这个动作结束,Bean 依然还没有初始化,后面会有大篇幅说初始化过程,
            // 在 Spring 容器启动的最后,会 预初始化 所有的 singleton beans
            if (hasBeanCreationStarted()) {
                // 在启动时集合无法再被修改(为了稳定的迭代)
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            // 最正常的应该是进到这个分支
            else {
                // 将 BeanDefinition 放到这个 map 中,这个 map 保存了所有的 BeanDefinition
                this.beanDefinitionMap.put(beanName, beanDefinition);
                // 这是个 ArrayList,所以会按照 bean 配置的顺序保存每一个注册的 Bean 的名字
                this.beanDefinitionNames.add(beanName);
                // 这是个 LinkedHashSet,代表的是手动注册的 singleton bean,
                // 注意这里是 remove 方法,到这里的 Bean 当然不是手动注册的
                // 手动指的是通过调用以下方法注册的 bean :
                // registerSingleton(String beanName, Object singletonObject)
                // 这不是重点,解释只是为了不让大家疑惑。Spring 会在后面"手动"注册一些 Bean,
                // 如 "environment"、"systemProperties" 等 bean,我们自己也可以在运行时注册 Bean 到容器中的
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        if (existingDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
}

五、总结

到了这里,终于创建了BeanDefinition并将其注册到了BeanFactory中,让我们回顾一下,本章的这些操作是从哪里开始的

1.refresh()

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();

            ……
        }
}

2.obtainFreshBeanFactory()

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

3.refreshBeanFactory()

protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            ……
            loadBeanDefinitions(beanFactory);
            ……
        }
        catch (IOException ex) {
            ……
        }
    }

不难发现,到此refresh中的obtainFreshBeanFactory()方法已经执行结束并返回了,这一过程中进行了BeanFactory的创建,BeanDefinition从xml中的读取、解析、创建、加强、注册等一系列操作,现在回头想想竟然还有一些意犹未尽,interesting~

六、预告

下篇要开始写refresh方法的下一级调用了,真是期待啊~

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