二、基于Xml 的IOC 容器的初始化

IOC 容器的初始化包括BeanDefinition 的Resource 定位、加载和注册这三个基本的过程。
其继承体系如下图所示:


image.png

ApplicationContext 允许上下文嵌套,通过保持父上下文可以维持一个上下文体系。对于Bean 的查找可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的Spring应用提供了一个共享的Bean 定义环境。

1、寻找入口

我们用的比较多的ClassPathXmlApplicationContext,通过main()方法启动:

ApplicationContext app = new ClassPathXmlApplicationContext("application.xml");

先看其构造函数的调用:

    public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[]{configLocation}, true, (ApplicationContext)null);
    }

其实际调用的构造函数为:

    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
        super(parent);
        this.setConfigLocations(configLocations);
        if (refresh) {
            this.refresh();
        }
    }

还有像AnnotationConfigApplicationContext 、FileSystemXmlApplicationContext 、
XmlWebApplicationContext 等都继承自父容器AbstractApplicationContext 主要用到了装饰器模式和策略模式,最终都是调用refresh()方法。

2、获得配置路径

在创建ClassPathXmlApplicationContext 容器时,构造方法做以下两项重要工作:

首先,调用父类容器的构造方法(super(parent)方法)为容器设置好Bean 资源加载器。

然后, 再调用父类AbstractRefreshableConfigApplicationContext 的setConfigLocations(configLocations)方法设置Bean 配置信息的定位路径。

通过追踪ClassPathXmlApplicationContext 的继承体系, 发现其父类的父类AbstractApplicationContext 中初始化IOC 容器所做的主要源码如下:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext {
    //静态初始化块,在整个容器创建过程中只执行一次
    static {
        //为了避免应用程序在Weblogic8.1 关闭时出现类加载异常加载问题,加载IOC 容
        //器关闭事件(ContextClosedEvent)类
        ContextClosedEvent.class.getName();
    }

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

    public AbstractApplicationContext(@Nullable ApplicationContext parent) {
        this();
        setParent(parent);
    }

    //获取一个Spring Source 的加载器用于读入Spring Bean 配置信息
    protected ResourcePatternResolver getResourcePatternResolver() {
        //AbstractApplicationContext 继承DefaultResourceLoader,因此也是一个资源加载器
        //Spring 资源加载器,其getResource(String location)方法用于载入资源
        return new PathMatchingResourcePatternResolver(this);
    }
...
}

AbstractApplicationContext 的默认构造方法中有调用PathMatchingResourcePatternResolver 的构造方法创建Spring 资源加载器:

    public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
        Assert.notNull(resourceLoader, "ResourceLoader must not be null");
        //设置Spring 的资源加载器
        this.resourceLoader = resourceLoader;
    }

在设置容器的资源加载器之后,接下来ClassPathXmlApplicationContext 执行setConfigLocations()方法通过调用其父类AbstractRefreshableConfigApplicationContext 的方法进行对Bean 配置信息的定位,该方法的源码如下:

    /**
     * Set the config locations for this application context in init-param style,
     * i.e. with distinct locations separated by commas, semicolons or whitespace.
     * <p>If not set, the implementation may use a default as appropriate.
     */
    //处理单个资源文件路径为一个字符串的情况
    public void setConfigLocation(String location) {
        //String CONFIG_LOCATION_DELIMITERS = ",; /t/n";
        //即多个资源文件路径之间用” ,; \t\n”分隔,解析成数组形式
        setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
    }

    /**
     * Set the config locations for this application context.
     * <p>If not set, the implementation may use a default as appropriate.
     */
    //解析Bean 定义资源文件的路径,处理多个资源文件字符串数组
    public void setConfigLocations(@Nullable 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++) {
                // resolvePath 为同一个类中将字符串解析为路径的方法
                this.configLocations[i] = resolvePath(locations[i]).trim();
            }
        } else {
            this.configLocations = null;
        }
    }

通过这两个方法的源码我们可以看出,我们既可以使用一个字符串来配置多个Spring Bean 配置信息,也可以使用字符串数组,即下面两种方式都是可以的:

ClassPathResource res = new ClassPathResource("a.xml,b.xml");

多个资源文件路径之间可以是用” , ; \t\n”等分隔。

ClassPathResource res =new ClassPathResource(new String[]{"a.xml","b.xml"});

至此,SpringIOC 容器在初始化时将配置的Bean 配置信息定位为Spring 封装的Resource。

3、开始启动

SpringIOC 容器对Bean 配置资源的载入是从refresh()函数开始的,refresh()是一个模板方法,规定了IOC 容器的启动流程, 有些逻辑要交给其子类去实现。它对Bean 配置资源进行载入ClassPathXmlApplicationContext 通过调用其父类AbstractApplicationContext 的refresh()函数启动整个IOC 容器对Bean 定义的载入过程,现在我们来详细看看refresh()中的逻辑处理:

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            //1、调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
            prepareRefresh();
            // Tell the subclass to refresh the internal bean factory.
            //2、告诉子类启动refreshBeanFactory()方法,Bean 定义资源文件的载入从
            //子类的refreshBeanFactory()方法启动
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            // Prepare the bean factory for use in this context.
            //3、为BeanFactory 配置容器特性,例如类加载器、事件处理器等
            prepareBeanFactory(beanFactory);
            try {
                // Allows post-processing of the bean factory in context subclasses.
                //4、为容器的某些子类指定特殊的BeanPost 事件处理器
                postProcessBeanFactory(beanFactory);
                // Invoke factory processors registered as beans in the context.
                //5、调用所有注册的BeanFactoryPostProcessor 的Bean
                invokeBeanFactoryPostProcessors(beanFactory);
                // Register bean processors that intercept bean creation.
                //6、为BeanFactory 注册BeanPost 事件处理器.
                //BeanPostProcessor 是Bean 后置处理器,用于监听容器触发的事件
                registerBeanPostProcessors(beanFactory);
                // Initialize message source for this context.
                //7、初始化信息源,和国际化相关.
                initMessageSource();
                // Initialize event multicaster for this context.
                //8、初始化容器事件传播器.
                initApplicationEventMulticaster();
                // Initialize other special beans in specific context subclasses.
                //9、调用子类的某些特殊Bean 初始化方法
                onRefresh();
                // Check for listener beans and register them.
                //10、为事件传播器注册事件监听器.
                registerListeners();
                // Instantiate all remaining (non-lazy-init) singletons.
                //11、初始化所有剩余的单例Bean
                finishBeanFactoryInitialization(beanFactory);
                // Last step: publish corresponding event.
                //12、初始化容器的生命周期事件处理器,并发布容器的生命周期事件
                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.
                //13、销毁已创建的Bean
                destroyBeans();
                // Reset 'active' flag.
                //14、取消refresh 操作,重置容器的同步标识.
                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...
                //15、重设公共缓存
                resetCommonCaches();
            }
        }
    }

refresh()方法主要为IOC 容器Bean 的生命周期管理提供条件,Spring IOC 容器载入Bean 配置信息从其子类容器的refreshBeanFactory() 方法启动, 所以整个refresh() 中“ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();”这句以后代码的都是注册容器的信息源和生命周期事件,我们前面说的载入就是从这句代码开始启动。

refresh()方法的主要作用是:在创建IOC 容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh 之后使用的是新建立起来的IOC 容器。它类似于对IOC 容器的重启,在新建立好的容器中对容器进行初始化,对Bean 配置资源进行载入。

4、创建容器

obtainFreshBeanFactory()方法调用子类容器的refreshBeanFactory()方法,启动容器载入Bean 配置信息的过程,代码如下:

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        //这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法,具体实现调用子类容器的refreshBeanFactory()方法
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

AbstractApplicationContext 类中只抽象定义了refreshBeanFactory()方法,容器真正调用的是其子类AbstractRefreshableApplicationContext 实现的refreshBeanFactory()方法,方法的源码如下:

    protected final void refreshBeanFactory() throws BeansException {
        //如果已经有容器,销毁容器中的bean,关闭容器
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            //创建IOC 容器
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            //对IOC 容器进行定制化,如设置启动参数,开启注解的自动装配等
            customizeBeanFactory(beanFactory);
            //调用载入Bean 定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions 方法,具体的实现调用子类容器
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        } catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

在这个方法中,先判断BeanFactory 是否存在,如果存在则先销毁beans 并关闭beanFactory,接着创建DefaultListableBeanFactory,并调用loadBeanDefinitions(beanFactory)装载bean 定义。

5、载入配置路径

AbstractRefreshableApplicationContext 中只定义了抽象的loadBeanDefinitions 方法,容器真正调用的是其子类AbstractXmlApplicationContext 对该方法的实现,AbstractXmlApplicationContext的主要源码如下:
loadBeanDefinitions() 方法同样是抽象方法, 是由其子类实现的, 也即在AbstractXmlApplicationContext 中。

public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
    ...

    //实现父类抽象的载入Bean 定义方法
    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        //创建XmlBeanDefinitionReader,即创建Bean 读取器,并通过回调设置到容器中去,容器使用该读取器读取Bean 配置资源
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        //为Bean 读取器设置Spring 资源加载器,AbstractXmlApplicationContext 的
        //祖先父类AbstractApplicationContext 继承DefaultResourceLoader,因此,容器本身也是一个资源加载器
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        //为Bean 读取器设置SAX xml 解析器
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        //当Bean 读取器读取Bean 定义的Xml 资源文件时,启用Xml 的校验机制
        initBeanDefinitionReader(beanDefinitionReader);
        //Bean 读取器真正实现加载的方法
        loadBeanDefinitions(beanDefinitionReader);
    }

    protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
        reader.setValidating(this.validating);
    }

    //Xml Bean 读取器加载Bean 配置资源
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        //获取Bean 配置资源的定位
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            //Xml Bean 读取器调用其父类AbstractBeanDefinitionReader 读取定位的Bean 配置资源
            reader.loadBeanDefinitions(configResources);
        }
        // 如果子类中获取的Bean 配置资源定位为空,则获取ClassPathXmlApplicationContext
        // 构造方法中setConfigLocations 方法设置的资源
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            //Xml Bean 读取器调用其父类AbstractBeanDefinitionReader 读取定位的Bean 配置资源
            reader.loadBeanDefinitions(configLocations);
        }
    }

    //这里又使用了一个委托模式,调用子类的获取Bean 配置资源定位的方法
    //该方法在ClassPathXmlApplicationContext 中进行实现,对于我们
    //举例分析源码的ClassPathXmlApplicationContext 没有使用该方法
    @Nullable
    protected Resource[] getConfigResources() {
        return null;
    }
}

以XmlBean 读取器的其中一种策略XmlBeanDefinitionReader 为例。XmlBeanDefinitionReader 调用其父类AbstractBeanDefinitionReader 的reader.loadBeanDefinitions()方法读取Bean 配置资源。
由于我们使用ClassPathXmlApplicationContext 作为例子分析,因此getConfigResources 的返回值为null,因此程序执行reader.loadBeanDefinitions(configLocations)分支。

6、分配路径处理策略

在XmlBeanDefinitionReader 的抽象父类AbstractBeanDefinitionReader 中定义了载入过程。
AbstractBeanDefinitionReader 的loadBeanDefinitions()方法源码如下:

    //重载方法,调用下面的loadBeanDefinitions(String, Set<Resource>);方法
    @Override
    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(location, null);
    }

    public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws
            BeanDefinitionStoreException {
        //获取在IOC 容器初始化过程中设置的资源加载器
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException(
                    "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        }
        if (resourceLoader instanceof ResourcePatternResolver) {
            // Resource pattern matching available.
            try {
                //将指定位置的Bean 配置信息解析为Spring IOC 容器封装的资源
                //加载多个指定位置的Bean 配置信息
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                //委派调用其子类XmlBeanDefinitionReader 的方法,实现加载功能
                int loadCount = loadBeanDefinitions(resources);
                if (actualResources != null) {
                    for (Resource resource : resources) {
                        actualResources.add(resource);
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                }
                return loadCount;
            } catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "Could not resolve bean definition resource pattern [" + location + "]", ex);
            }
        } else {
            // Can only load single resources by absolute URL.
            //将指定位置的Bean 配置信息解析为Spring IOC 容器封装的资源
            //加载单个指定位置的Bean 配置信息
            Resource resource = resourceLoader.getResource(location);
            //委派调用其子类XmlBeanDefinitionReader 的方法,实现加载功能
            int loadCount = loadBeanDefinitions(resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
            }
            return loadCount;
        }
    }

    //重载方法,调用loadBeanDefinitions(String);
    @Override
    public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
        Assert.notNull(locations, "Location array must not be null");
        int counter = 0;
        for (String location : locations) {
            counter += loadBeanDefinitions(location);
        }
        return counter;
    }

AbstractRefreshableConfigApplicationContext 的loadBeanDefinitions(Resource...resources) 方法实际上是调用AbstractBeanDefinitionReader 的loadBeanDefinitions()方法。
从对AbstractBeanDefinitionReader 的loadBeanDefinitions()方法源码分析可以看出该方法就做了两件事:
首先,调用资源加载器的获取资源方法resourceLoader.getResource(location),获取到要加载的资源。
其次,真正执行加载功能是其子类XmlBeanDefinitionReader 的loadBeanDefinitions()方法。在loadBeanDefinitions()方法中调用了AbstractApplicationContext 的getResources()方法,跟进去之后发现getResources()方法其实定义在ResourcePatternResolver 中,此时,我们有必要来看一下ResourcePatternResolver 的全类图:


image.png

image.png

从上面可以看到ResourceLoader 与ApplicationContext 的继承关系,可以看出其实际调用的是DefaultResourceLoader 中的getSource() 方法定位Resource , 因为ClassPathXmlApplicationContext 本身就是DefaultResourceLoader 的实现类,所以此时又回到了ClassPathXmlApplicationContext 中来。

7、解析配置文件路径

XmlBeanDefinitionReader 通过调用ClassPathXmlApplicationContext 的父类DefaultResourceLoader 的getResource()方法获取要加载的资源
//TODO添加源码解析

8、开始读取配置内容

继续回到XmlBeanDefinitionReader 的loadBeanDefinitions(Resource …)方法看到代表bean 文件的资源定义以后的载入过程。
//TODO添加源码解析

9、准备文档对象

DocumentLoader 将Bean 配置资源转换成Document 对象
//TODO添加源码解析

10、分配解析策略

XmlBeanDefinitionReader 类中的doLoadBeanDefinition()方法是从特定XML 文件中实际载入Bean 配置资源的方法,该方法在载入Bean 配置资源之后将其转换为Document 对象,接下来调用registerBeanDefinitions() 启动Spring IOC 容器对Bean 定义的解析过程。
//TODO添加源码解析

11、将配置载入内存

BeanDefinitionDocumentReader 接口通过registerBeanDefinitions() 方法调用其实现类DefaultBeanDefinitionDocumentReader 对Document 对象进行解析。
//TODO添加源码解析

12、载入<bean>元素

Bean 配置信息中的<import>和<alias>元素解析在DefaultBeanDefinitionDocumentReader 中已经完成,对Bean 配置信息中使用最多的<bean>元素交由BeanDefinitionParserDelegate 来解析。
//TODO添加源码解析

13、载入<property>元素

BeanDefinitionParserDelegate 在解析<Bean>调用parsePropertyElements()方法解析<Bean>元素中的<property>属性子元素。
//TODO添加源码解析

14、载入<property>的子元素

在BeanDefinitionParserDelegate 类中的parsePropertySubElement()方法对<property>中的子元素解析。
//TODO添加源码解析

15、载入<list>的子元素

在BeanDefinitionParserDelegate 类中的parseListElement()方法就是具体实现解析<property>元素中的<list>集合子元素。
//TODO添加源码解析

16、分配注册策略

让我们继续跟踪程序的执行顺序,接下来我们来分析DefaultBeanDefinitionDocumentReader 对Bean 定义转换的Document 对象解析的流程中, 在其parseDefaultElement() 方法中完成对Document 对象的解析后得到封装BeanDefinition 的BeanDefinitionHold 对象, 然后调用BeanDefinitionReaderUtils 的registerBeanDefinition() 方法向IOC 容器注册解析的Bean 。

BeanDefinitionReaderUtils 的注册的源码如下:

    //将解析的BeanDefinitionHold 注册到容器中
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {
        //获取解析的BeanDefinition 的名称
        String beanName = definitionHolder.getBeanName();
        //向IOC 容器注册BeanDefinition
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
        //如果解析的BeanDefinition 有别名,向容器为其注册别名
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

当调用BeanDefinitionReaderUtils 向IOC 容器注册解析的BeanDefinition 时,真正完成注册功能的是DefaultListableBeanFactory。

17、向容器注册

DefaultListableBeanFactory 中使用一个HashMap 的集合对象存放IOC 容器中注册解析的BeanDefinition,向IOC 容器注册的主要源码如下:


image.png
    //存储注册信息的BeanDefinition
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    //向IOC 容器注册解析的BeanDefiniton
    @Override
    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");
        //校验解析的BeanDefiniton
        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition) beanDefinition).validate();
            } catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }
        BeanDefinition oldBeanDefinition;
        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        if (oldBeanDefinition != null) {
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                                "': There is already [" + oldBeanDefinition + "] bound.");
            } 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 (!beanDefinition.equals(oldBeanDefinition)) {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            } else {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            this.beanDefinitionMap.put(beanName, beanDefinition);
        } else {
            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 {
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }
        //检查是否有同名的BeanDefinition 已经在IOC 容器中注册
        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            //重置所有已经注册过的BeanDefinition 的缓存
            resetBeanDefinition(beanName);
        }
    }

至此,Bean 配置信息中配置的Bean 被解析过后,已经注册到IOC 容器中,被容器管理起来,真正完成了IOC 容器初始化所做的全部工作。现在IOC 容器中已经建立了整个Bean 的配置信息,这些BeanDefinition 信息已经可以使用,并且可以被检索,IOC 容器的作用就是对这些注册的Bean 定义信息进行处理和维护。这些的注册的Bean 定义信息是IOC 容器控制反转的基础,正是有了这些注册的数据,容器才可以进行依赖注入。

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