spring学习(三)——BeanDefinition的拼装
参考文章:
编程式加载spring
//获取资源
ClassPathResource resource = new ClassPathResource("bean.xml");
//获取 BeanFactory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//根据新建的 BeanFactory 创建一个 BeanDefinitionReader 对象,该 Reader 对象为资源的解析器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
//装载资源
reader.loadBeanDefinitions(resource);
- 资源定位。初始化 IoC 容器的第一步就是需要定位这个外部资源。。
-
装载。装载就是 BeanDefinition 的载入。BeanDefinitionReader 读取、解析 Resource 资源,也就是将用户定义的 Bean 表示成 IoC 容器的内部数据结构:BeanDefinition 。
- 在 IoC 容器内部维护着一个 BeanDefinition Map 的数据结构
- 在配置文件中每一个
<bean>
都对应着一个 BeanDefinition 对象。
-
注册。向 IoC 容器注册在第二步解析好的 BeanDefinition,这个过程是通过 BeanDefinitionRegistry 接口来实现的。在 IoC 容器内部其实是将第二个过程解析得到的 BeanDefinition 注入到一个 HashMap 容器中,IoC 容器就是通过这个 HashMap 来维护这些 BeanDefinition 的。
- 在这里需要注意的一点是这个过程并没有完成依赖注入(Bean 创建),Bean 创建是发生在应用第一次调用
#getBean(...)
方法,向容器索要 Bean 时。 - 当然我们可以通过设置预处理,即对某个 Bean 设置
lazyinit = false
属性,那么这个 Bean 的依赖注入就会在容器初始化的时候完成。
- 在这里需要注意的一点是这个过程并没有完成依赖注入(Bean 创建),Bean 创建是发生在应用第一次调用
类的装载
BeanDefinitionReader主要进行类的装载的接口
类图
主要实现类
XmlBeanDefinitionReader
loadBeanDefinitions
加载资源的真正实现
先进行编码后在 loadBeanDefinitions(new EncodedResource(resource))中进行最终加载
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//主要是为了对 Resource 进行编码,保证内容读取的正确性
return loadBeanDefinitions(new EncodedResource(resource));
}
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();
}
}
}
doLoadBeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//获取资源的doc实例
Document doc = doLoadDocument(inputSource, resource);
//注册成BeanDefinitions
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
可以看到spring加载bean的顺序是:
- 先加载resource,
- 然后加载document,
- 然后注册beanDefinitions
doLoadDocument
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
//首先验证资源 getValidationModeForResource(resource)
//然后拼装Document
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
类的注册
类的注册在XmlBeanDefinitionReader里面的方
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;
}
主要方法
createBeanDefinitionDocumentReader
- 实例化documentReader类
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanUtils.instantiateClass(this.documentReaderClass);
}
createReaderContext
- 创建 XmlReaderContext 对象
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
registerBeanDefinitions
- 注册 BeanDefinition ,在接口 BeanDefinitionDocumentReader
public interface BeanDefinitionDocumentReader {
/**
* Read bean definitions from the given DOM document and
* register them with the registry in the given reader context.
* @param doc the DOM document
* @param readerContext the current context of the reader
* (includes the target registry and the resource being parsed)
* @throws BeanDefinitionStoreException in case of parsing errors
* doc 文档对象
* readerContext xml解析的上下文
*/
void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
throws BeanDefinitionStoreException;
}
DefaultBeanDefinitionDocumentReader
BeanDefinitionDocumentReader 有且只有一个默认实现类 DefaultBeanDefinitionDocumentReader
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
//获得文档对象标签,执行注册
doRegisterBeanDefinitions(doc.getDocumentElement());
}
/**
* Register each bean definition within the given root {@code <beans/>} element.
*/
@SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...)
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;
}
BeanDefinitionParserDelegate 它负责解析 BeanDefinition
parseBeanDefinitions
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)) {
parseDefaultElement(ele, delegate);
}
// 如果该节点非默认命名空间,执行自定义解析
else {
delegate.parseCustomElement(ele);
}
}
}
}
// 如果根节点非默认命名空间,执行自定义解析
else {
delegate.parseCustomElement(root);
}
}
默认的解析逻辑
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//import
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//alias
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//bean
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//beans
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
另外,
XmlBeanDefinitionReader#doLoadBeanDefinitions(InputSource inputSource, Resource resource)
方法,整体时序图如下:
- 红框部分,就是 BeanDefinition 的解析过程。