参考文章:

http://www.iocoder.cn

IoC 容器的初始化过程

  1. Resource 定位
  2. BeanDefinition 的载入和解析
  3. BeanDefinition 注册

Resource 定位

我们一般用外部资源来描述 Bean 对象,所以在初始化 IoC 容器的第一步就是需要定位这个外部资源。

spring提供了两个接口:Resource、ResourceLoader

  • Resource 接口是 Spring 统一资源的抽象接口
  • ResourceLoader 则是 Spring 资源加载的统一抽象。

ResourceLoader 初始化

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

		// Determine ResourceLoader to use.
		// 如果设置了 ResourceLoader 则用设置的,否则使用 PathMatchingResourcePatternResolver 
		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();
		}
	}

 

BeanDefinition 的载入和解析

 

Resource 定位。

BeanDefinition 的装载和解析。装载就是 BeanDefinition 的载入。BeanDefinitionReader 读取、解析 Resource 资源,也就是将用户定义的 Bean 表示成 IoC 容器的内部数据结构:BeanDefinition 。

  • 在 IoC 容器内部维护着一个 BeanDefinition Map 的数据结构
  • 在配置文件中每一个 <bean> 都对应着一个 BeanDefinition 对象。

开启 BeanDefinition 的解析过程

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isTraceEnabled()) {
			logger.trace("Loading XML bean definitions from " + encodedResource);
		}
		//获取已经加载过的资源,然后将 encodedResource 加入其中
		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		//注意是资源,不是bean
		//resourcesCurrentlyBeingLoaded 中已经存在该资源,则抛出 BeanDefinitionStoreException 异常
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			//从 EncodedResource 获取封装的 Resource ,并从 Resource 中获取其中的 InputStream
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				// 核心逻辑部分,执行加载 BeanDefinition
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				inputStream.close();
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			// 从缓存中剔除该资源 ,为了防止一个 EncodedResource 在加载时,还没加载完成,又加载自身,从而导致死循环
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove();
			}
		}
	}
  • 根据 xml 解析源获取相应的 Document 对象。详细解析。
  • 调用 #registerBeanDefinitions(Document doc, Resource resource) 方法,开启 BeanDefinition 的解析注册过程

转换为 Document 对象

@Override
	public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
			ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
		//创建DocumentBuilder的工厂
		DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
		if (logger.isTraceEnabled()) {
			logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
		}
		//创建DocumentBuilder实例
		DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
		//解析数据并生成Document
		return builder.parse(inputSource);
	}

注册 BeanDefinition 流程

BeanDefinition 注册。向 IoC 容器注册在第二步解析好的 BeanDefinition,这个过程是通过 BeanDefinitionRegistry 接口来实现的。在 IoC 容器内部其实是将第二个过程解析得到的 BeanDefinition 注入到一个 HashMap 容器中,IoC 容器就是通过这个 HashMap 来维护这些 BeanDefinition 的。

  • 在这里需要注意的一点是这个过程并没有完成依赖注入(Bean 创建),Bean 创建是发生在应用第一次调用 #getBean(...) 方法,向容器索要 Bean 时。
  • 当然我们可以通过设置预处理,即对某个 Bean 设置 lazyinit = false 属性,那么这个 Bean 的依赖注入就会在容器初始化的时候完成。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//创建 BeanDefinitionDocumentReader 对象
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//获取已注册的 BeanDefinition 数量
		int countBefore = getRegistry().getBeanDefinitionCount();
		//注册 BeanDefinition(doc,创建 XmlReaderContext 对象)
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		//计算新注册的 BeanDefinition 数量
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

对 Document 对象的解析

从 Document 对象中获取根元素 root,然后调用 #doRegisterBeanDefinitions(Element root)` 方法,开启真正的解析过程

protected void doRegisterBeanDefinitions(Element root) {
		BeanDefinitionParserDelegate parent = this.delegate;
		//创建 BeanDefinitionParserDelegate 对象,并进行设置到 delegate
		//BeanDefinitionParserDelegate 是一个重要的类,它负责解析 BeanDefinition
		this.delegate = createDelegate(getReaderContext(), root, parent);
		//检查 <beans /> 根标签的命名空间是否为空,或者是 http://www.springframework.org/schema/beans
		if (this.delegate.isDefaultNamespace(root)) {
			//处理 profile 属性。可参见《Spring3自定义环境配置 <beans profile="">》http://nassir.iteye.com/blog/1535799
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
				//使用分隔符切分,可能有多个 profile 。
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				// 如果所有 profile 都无效,则不进行注册
				// We cannot use Profiles.of(...) since profile expressions are not supported
				// in XML config. See SPR-12458 for details.
				if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
					if (logger.isDebugEnabled()) {
						logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
								"] not matching: " + getReaderContext().getResource());
					}
					return;
				}
			}
		}
		//解析前处理
		preProcessXml(root);
		//解析
		parseBeanDefinitions(root, this.delegate);
		//解析后处理
		postProcessXml(root);
		//设置 delegate 回老的 BeanDefinitionParserDelegate 对象
		this.delegate = parent;
	}

注册 BeanDefinition

则将 Document 对象里面的 Bean 标签解析成了一个个的 BeanDefinition ,下一步则是将这些 BeanDefinition 注册到 IoC 容器中

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		// 进行 bean 元素解析。
		// 如果解析成功,则返回 BeanDefinitionHolder 对象。而 BeanDefinitionHolder 为 name 和 alias 的 BeanDefinition 对象
		// 如果解析失败,则返回 null 。
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			//自定义标签处理
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// Register the final decorated instance.
				// 注册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.
			// 发出响应事件,通知相关的监听器,已完成该 Bean 标签的解析。
			// Send registration event.
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}

 

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

		// 注册 beanName
		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// 再通过注册别名 alias 和 beanName 的映射。详细解析
		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}

 

整个流程

IoC 的初始化过程就已经完成了,从 Bean 资源的定位,转换为 Document 对象,接着对其进行解析,最后注册到 IoC 容器中,都已经完美地完成了。现在 IoC 容器中已经建立了整个 Bean 的配置信息,这些 Bean 可以被检索、使用、维护,他们是控制反转的基础,是后面注入 Bean 的依赖