IoC容器初始化过程

IoC容器初始化主要包括三个过程:

  1. BeanDefinition 的 Resource 定位

  2. BeanDefinition 的载入

  3. BeanDefinition 在 Ioc 容器中的注册

三个过程都与BeanDefinition有关,那么我们先来看一下什么是BeanDefinition 。

BeanDefinition 是 Spring 内部的一个接口,定义了 Bean 描述的基本规范,是Bean在spring中的存在形式。BeanDefinition 接口的方法覆盖了 Spring 构造 Bean 需要的所有信息,是一个什么样的类型,构造器有哪些实参,属性的值注入哪些值,使用哪个 FactoryBean 来获取 Bean等。
初始化的目的就是找到应用中以XML方式、注解方式或是其他方式定义的bean,然后将其加载到容器中,并以BeanDefinition的形式来描述。以便IoC容器来管理

我们通过FileXSystemXmlApplicationContext的实现来说明容器的初始化过程,其继承结构如下:
在这里插入图片描述
首先,我们来看FileXSystemXmlApplicationContext的构造函数来看其具体的初始化过程:

public FileXSystemXmlApplicationContext(String[] configLocations, boolean refresh, 
			ApplicationContext parent) throws BeansException {
			super(parent);
			setConfigLocations(configLocations);
			if (refresh) {
				// 启动BeanDefinition的载入过程
				refresh();
			}
	}

根据以上构造函数,可以知道是由refresh()函数来启动初始化的。refresh()函数的调用过程如下:
在这里插入图片描述
在refreshBeanFactory的实现按如下:

@Override
protected final void refreshBeanFactory() throws BeansException {
    // 如果已经存在beanfactory那就销毁掉bean并把工厂关了,避免对接下来的初始化造成影响
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        // 这里创建了一个DefaultListableBeanFactory
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        // 设置唯一id,用于序列化
        beanFactory.setSerializationId(getId());
        // 自定义bean工厂
        customizeBeanFactory(beanFactory);
        // 向工厂中加载BeanDefinition,这个很重要,【继续往里走】
        loadBeanDefinitions(beanFactory);
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

资源定位

其实资源定位是从refreshBeanFactory方法中的 loadBeanDefinitions(beanFactory)开始,一步一步向下调用,最终调用到下面的loadBeanDefinitions方法,完成资源定位:

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
    // 获取资源加载器
    ResourceLoader resourceLoader = getResourceLoader();
    // 如果为空就抛异常
    if (resourceLoader == null) {
        throw new BeanDefinitionStoreException(
            "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
    }
    // 这个是用来解析classpath*:这种的路径,可以是多个配置文件
    if (resourceLoader instanceof ResourcePatternResolver) {
        // Resource pattern matching available.
        try {
            //到这里getResource【完成了具体的定位】
            Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
            // 开始载入BeanDefinition
            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 {
        // 到这里getResource,resource接口中封装了很多与I/O相关的操作
        // 至此【完成了具体的定位】
        Resource resource = resourceLoader.getResource(location);
        // 开始载入BeanDefinition【继续往里跳】
        int loadCount = loadBeanDefinitions(resource);
        if (actualResources != null) {
            actualResources.add(resource);
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
        }
        return loadCount;
    }
}

资源定位,由 ResourceLoader 通过统一的 Resource 接口来完成,这个 Resource 对各种形式的 BeanDefinition 的使用都提供了统一的接口。Spring 提供了适用于各种场景的默认实现,如类路径下的资源可以用 ClassPathResource、网络上的资源可以用 UrlResource。

载入

继续往下走,有上述代码的 int loadCount = loadBeanDefinitions(resource);继续往下走可以走到XMLBeanDefinitionReader中找到具体的载入实现:

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());
    }
    // 获取已经加载的Resource
    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    if (currentResources == null) {
        currentResources = new HashSet<EncodedResource>(4);
        this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }
    // 解决重复依赖问题,encodedResource的equals方法已经被重写
    if (!currentResources.add(encodedResource)) {
        throw new BeanDefinitionStoreException(
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    }
    // 这里获取IO准备读取XML中的BeanDefinition
    try {
        InputStream inputStream = encodedResource.getResource().getInputStream();
        try {
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }
            // 【继续往里面跳】
            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();
        }
    }
}

BeanDefinition 的载入过程主要分成两部分:

  1. 首先通过调用XML的解析器得到得到document对象,
  2. 按照spring的Bean规则进行解析,具体在documentReader中实现。这里使用的documentReader是默认设置好的DefaultBeanDefinitionDcumentReader。这个DefaultBeanDefinitionDcumentReader的创建是在后面的方法来完成的。然后再完成BeanDefinition的处理,处理的结果由BeanDefinitionHolder对象来持有。

其实就是解析 Resource 对象得到 BeanDefinitionHolder 对象的过程。BeanDefinitionHolder 的作用是根据名称或者别名持有 beanDefinition,承载了 name 和 BeanDefinition 的映射信息。
Spring 并不是直接把 XML 文件的内容转换成 BeanDefinitionHolder。解析时先解析 XML 得到 Document 对象,Document 对象就是 XML 文件在内存里的存储形式。从 Document 对象提取数据用的是 BeanDefinitionDocumentReader。

对于 Document 对象中的一个节点 Element,是使用 BeanDefinitionParser 进行解析。开发者也可以自定义 BeanDefinitionParser 从而实现对 xml 配置的自定义解析,可以实现诸如自定义 XML 标签的功能。

注册

这个操作是通过调用 BeanDefinitionRegistry 接口来实现的。这个注册过程把载入过程中解析得到的 BeanDeifinition 向 Ioc 容器进行注册。具体方法如下:

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 {
            ((AbstractBeanDefinition) beanDefinition).validate();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                                   "Validation of bean definition failed", ex);
        }
    }
    // 为了保证数据一致性,注册时加个synchronized线程锁
    synchronized (this.beanDefinitionMap) {
        // 检查在IoC容器中是否有同名bean,有同名的还不让覆盖的就是抛异常
        BeanDefinition oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        if (oldBeanDefinition != null) {
            if (!this.allowBeanDefinitionOverriding) {
                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()) {
                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 {
            this.beanDefinitionNames.add(beanName);
            this.frozenBeanDefinitionNames = null;
        }
        // 把BeanDefinition装到如到beanDefinitionMap中
        // 【至此Spring IoC容器初始化完成~】
        // beanDeinitionMap是初始长度64的ConcurrentHashMap
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }

    resetBeanDefinition(beanName);
}

调用 registerBeanDefinition 方法解析 BeanDefinitionHolder 对象,按照 Bean 的名称、别名将 BeanDefinition 注册到 IoC 容器中,存储在 beanDefinitionMap 中。至此,容器的初始化基本完成。

参考:
https://zhuanlan.zhihu.com/p/50153734

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值