spring-framework 加载配置文件过程:
根据业务系统运行的环境,选择 ApplicationContext 接口合适的实现类,启动系统先初始化上下文环境,然后通过BeanDefinitionReader 的实现类读取Bean的配置文件。这里配置文件可以是 xml文件,properties文件,yml文件等,也可以是注解形式扫描配置。配置文件读取成功后,将相应的配置转换成 BeanDefinition 的对象实例保存在DefaultListableBeanFactory#beanDefinitionMap 中。紧接着,根据配置的 BeanFactoryPostProcessor 对象列表,进行 BeanDefinition 的扩展处理。( 例如 org.springframework.beans.factory.config.PlaceholderConfigurerSupport: ${} 配置转换成实际的值实现类。org.springframework.beans.factory.config.PropertyOverrideConfigurer: 将 beanName.property 替换成实际的值。 )
--> xml配置
|
ApplicationContext ---> BeanDefinitionReader -+----> yml配置 -----> BeanDefinition ---> DefaultListableBeanFactory ---> BeanFactoryPostProcessor
|
--> Annotation 扫描
这里以 XML 配置的BeanDefinition 进行源码加载分析:
一. BeanFactory 实例的创建
在 AbstractApplicationContext#obtainFreshBeanFactory 方法中,是进行 bean 处理的入口。
AbstractApplicationContext#obtainFreshBeanFactory
-> AbstractRefreshableApplicationContext#refreshBeanFactory
1.检测是否已经创建 BeanFactory 。如果创建了则销毁BeanFactory 相关的属性。
2.创建 BeanFactory 接口的默认实现类 DefaultListableBeanFactory 实例。
3.客户化 BeanFactory 处理。
4.加载 BeanDefinition 定义信息。
1 /** 2 * This implementation performs an actual refresh of this context's underlying 3 * bean factory, shutting down the previous bean factory (if any) and 4 * initializing a fresh bean factory for the next phase of the context's lifecycle. 5 */ 6 @Override 7 protected final void refreshBeanFactory() throws BeansException { 8 if (hasBeanFactory()) { 9 destroyBeans(); 10 closeBeanFactory(); 11 } 12 try { 13 DefaultListableBeanFactory beanFactory = createBeanFactory(); 14 beanFactory.setSerializationId(getId()); 15 customizeBeanFactory(beanFactory); 16 loadBeanDefinitions(beanFactory); 17 this.beanFactory = beanFactory; 18 } 19 catch (IOException ex) { 20 throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); 21 } 22 }
二. ApplicationContex 读取 BeanDefinition 的加载分析
这里我们分析 XML 形式配置的加载,因此通过 AbstractXmlApplicationContext 来分析源码。
AbstractApplicationContext#obtainFreshBeanFactory
-> AbstractRefreshableApplicationContext#refreshBeanFactory
--> AbstractXmlApplicationContext#loadBeanDefinitions(DefaultListableBeanFactory)
1.首先创建读取 BeanDefinition 的 BeanDefinitionReader 接口实现 XmlBeanDefinitionReader 实例,并设置环境信息。
2.通过 BeanDefinitionReader 实现读取 BeanDefinition 配置信息。
1 /** 2 * Loads the bean definitions via an XmlBeanDefinitionReader. 3 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader 4 * @see #initBeanDefinitionReader 5 * @see #loadBeanDefinitions 6 */ 7 @Override 8 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { 9 // Create a new XmlBeanDefinitionReader for the given BeanFactory. 10 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); 11 12 // Configure the bean definition reader with this context's 13 // resource loading environment. 14 beanDefinitionReader.setEnvironment(this.getEnvironment()); 15 beanDefinitionReader.setResourceLoader(this); 16 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); 17 18 // Allow a subclass to provide custom initialization of the reader, 19 // then proceed with actually loading the bean definitions. 20 initBeanDefinitionReader(beanDefinitionReader); 21 loadBeanDefinitions(beanDefinitionReader); 22 }
三. BeanDefinitionReader 读取,加载 BeanDefinition 配置分析
AbstractXmlApplicationContext#loadBeanDefinitions(DefaultListableBeanFactory)
--> AbstractXmlApplicationContext#loadBeanDefinitions(XmlBeanDefinitionReader)
--> AbstractBeanDefinitionReader#loadBeanDefinitions(String, Set<Resource>)
--> XmlBeanDefinitionReader#loadBeanDefinitions(EncodedResource)
--> XmlBeanDefinitionReader#doLoadBeanDefinitions(InputSource, Resource )
1.读取 XML 文件,获取到 Document 实例。为后续解析配置做准备。
2.向 BeanFactory 中注册 BeanDefinition 配置信息。
此处异常捕获比较多,学习异常规范处理。最底层的 Throwable 异常也捕获了,保证程序不会异常退出。
1 /** 2 * Actually load bean definitions from the specified XML file. 3 * @param inputSource the SAX InputSource to read from 4 * @param resource the resource descriptor for the XML file 5 * @return the number of bean definitions found 6 * @throws BeanDefinitionStoreException in case of loading or parsing errors 7 * @see #doLoadDocument 8 * @see #registerBeanDefinitions 9 */ 10 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) 11 throws BeanDefinitionStoreException { 12 13 try { 14 Document doc = doLoadDocument(inputSource, resource); 15 int count = registerBeanDefinitions(doc, resource); 16 if (logger.isDebugEnabled()) { 17 logger.debug("Loaded " + count + " bean definitions from " + resource); 18 } 19 return count; 20 } 21 catch (BeanDefinitionStoreException ex) { 22 throw ex; 23 } 24 catch (SAXParseException ex) { 25 throw new XmlBeanDefinitionStoreException(resource.getDescription(), 26 "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); 27 } 28 catch (SAXException ex) { 29 throw new XmlBeanDefinitionStoreException(resource.getDescription(), 30 "XML document from " + resource + " is invalid", ex); 31 } 32 catch (ParserConfigurationException ex) { 33 throw new BeanDefinitionStoreException(resource.getDescription(), 34 "Parser configuration exception parsing XML from " + resource, ex); 35 } 36 catch (IOException ex) { 37 throw new BeanDefinitionStoreException(resource.getDescription(), 38 "IOException parsing XML document from " + resource, ex); 39 } 40 catch (Throwable ex) { 41 throw new BeanDefinitionStoreException(resource.getDescription(), 42 "Unexpected exception parsing XML document from " + resource, ex); 43 } 44 }
四.BeanDefinitionDocumentReader 解析 BeanDefinition 配置分析
(一) BeanDefinitionDocumentReader 分析
XmlBeanDefinitionReader#doLoadBeanDefinitions
--> XmlBeanDefinitionReader#registerBeanDefinitions(Document, Resource)
--> BeanDefinitionDocumentReader#registerBeanDefinitionsregisterBeanDefinitions(Document, XmlReaderContext)
1. 创建 BeanDefinitionDocumentReader 对象实例。
2.创建 读取 BeanDefinition 配置信息Document的上下文(XmlReaderContext),为后续解析配置提供相关支持。
3. 解析并注册Document 中配置的 BeanDefinition 信息。在 DefaultBeanDefinitionDocumentReader 实现具体逻辑。
1 /** 2 * Register the bean definitions contained in the given DOM document. 3 * Called by {@code loadBeanDefinitions}. 4 * <p>Creates a new instance of the parser class and invokes 5 * {@code registerBeanDefinitions} on it. 6 * @param doc the DOM document 7 * @param resource the resource descriptor (for context information) 8 * @return the number of bean definitions found 9 * @throws BeanDefinitionStoreException in case of parsing errors 10 * @see #loadBeanDefinitions 11 * @see #setDocumentReaderClass 12 * @see BeanDefinitionDocumentReader#registerBeanDefinitions 13 */ 14 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { 15 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); 16 int countBefore = getRegistry().getBeanDefinitionCount(); 17 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 18 return getRegistry().getBeanDefinitionCount() - countBefore; 19 }
(二)BeanDefinitionDocumentReader 实现类 DefaultBeanDefinitionDocumentReader分析
BeanDefinitionDocumentReader#registerBeanDefinitionsregisterBeanDefinitions(Document, XmlReaderContext)
--> DefaultBeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
--> DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions(Element)
1. 根据 ReaderContext 信息,创建对应的 实例。
2. 检查 profile 配置信息。如果不匹配,则不解析当前root节点下的 BeanDefinition 配置。
3. 当前root节点 前置处理。
4. 解析当前 root 节点的 BeanDefinition 配置信息。
5. 当前root节点 后置处理。
(三) DefaultBeanDefinitionDocumentReader#parseBeanDefinitions 分析
DefaultBeanDefinitionDocumentReader#parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
--> DefaultBeanDefinitionDocumentReader#parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
--> BeanDefinitionParserDelegate#parseCustomElement(Element)
1. 判断 root 节点 DefaultNamespace 是否是 http://www.springframework.org/schema/beans 。
如果是,则遍历子节点进行解析。
当子节点 是 DefaultNamespace 。则使用 DefaultBeanDefinitionDocumentReader#parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) 解析节点。
否做使用 BeanDefinitionParserDelegate#parseCustomElement(Element) 解析节点。
2. 非默认 DefaultNamespace 值,则使用 BeanDefinitionParserDelegate#parseCustomElement(Element) 解析节点。
例如 AOP 配置节点解析。例如:<aop:config>
事物配置节点解析。例如:<tx:annotation-driven proxy-target-class="true" />
(四) 解析 DefaultNamespace 节点 BeanDefinition 信息。
DefaultBeanDefinitionDocumentReader#parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
--> DefaultBeanDefinitionDocumentReader#importBeanDefinitionResource(Element)
--> DefaultBeanDefinitionDocumentReader#processAliasRegistration(Element)
--> DefaultBeanDefinitionDocumentReader#processBeanDefinition(Element, BeanDefinitionParserDelegate)
--> DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions(Element)
1.依次解析 import 节点,alias 节点,bean节点, beans节点 定义的 BeanDefinition 信息。
(五) 解析 非默认命名空间节点 BeanDefinition 信息。
BeanDefinitionParserDelegate#parseCustomElement(Element)
--> BeanDefinitionParserDelegate#parseCustomElement(Element, BeanDefinition)
1.获取节点命名空间。
2.根据命名空间获取对应的解析器。
3. 解析节点 BeanDefinition 信息。
1 /** 2 * Parse a custom element (outside of the default namespace). 3 * @param ele the element to parse 4 * @param containingBd the containing bean definition (if any) 5 * @return the resulting bean definition 6 */ 7 @Nullable 8 public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { 9 String namespaceUri = getNamespaceURI(ele); 10 if (namespaceUri == null) { 11 return null; 12 } 13 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 14 if (handler == null) { 15 error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); 16 return null; 17 } 18 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); 19 }
五.BeanDefinitionDocumentReader 解析 bean 节点
XmlBeanDefinitionReader#loadBeanDefinitions(EncodedResource)
--> XmlBeanDefinitionReader#doLoadBeanDefinitions(InputSource, Resource)
--> XmlBeanDefinitionReader#registerBeanDefinitions(Document, Resource)
--> DefaultBeanDefinitionDocumentReader#registerBeanDefinitions(Document, XmlReaderContext)
--> DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions(Element)
--> DefaultBeanDefinitionDocumentReader#parseBeanDefinitions(Element , BeanDefinitionParserDelegate)
--> DefaultBeanDefinitionDocumentReader#parseDefaultElement(Element, BeanDefinitionParserDelegate)
--> DefaultBeanDefinitionDocumentReader#processBeanDefinition(Element, BeanDefinitionParserDelegate)
1.使用 BeanDefinitionParserDelegate 解析 Bean 配置节点,得到保存 BeanDefinition 信息的辅助对象 BeanDefinitionHolder 实例。
2.当 解析的 Bean 配置 BeanDefinitionHolder 实例不为空,则进行 Bean 配置 后续处理。
a. 当 Bean 配置节点 找到 其他 Namespace 对应 处理器(NamespaceHandler),则进行特殊化处理。
b. 向 BeanFactory 中注册解析成功的 BeanDefinition 定义。
c.发送 BeanDefinition 解析成功事件 BeanComponentDefinition。
1 /** 2 * Process the given bean element, parsing the bean definition 3 * and registering it with the registry. 4 */ 5 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { 6 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 7 if (bdHolder != null) { 8 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 9 try { 10 // Register the final decorated instance. 11 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); 12 } 13 catch (BeanDefinitionStoreException ex) { 14 getReaderContext().error("Failed to register bean definition with name '" + 15 bdHolder.getBeanName() + "'", ele, ex); 16 } 17 // Send registration event. 18 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); 19 } 20 }
六. BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition) 解析 bean 节点
DefaultBeanDefinitionDocumentReader#processBeanDefinition(Element, BeanDefinitionParserDelegate)
--> BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element)
--> BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element, BeanDefinition)
1.解析 Element 元素的 id 和 name 属性,得到 beanName 和 aliases。
2.检查 beanName 是否唯一。
3. 创建 AbstractBeanDefinition 对象实例 bd ,并通过依次解析 Element 元素的属性给 bd 赋值。
依次解析子节点 meta, lookup-method, replaced-method, constructor-arg, property, qualifier,并将解析后的值添加到 bd 相关变量。
设置 bd 解析资源的相关信息。
4. 如果 3 解析失败,则返回的 实例BeanDefinition 为 null,则这里也直接返回。
5. 当上述步骤解析的 beanName 为 空时,使用指定方式生成 beanName。
当前 bean 是在其他Bean 内部时, 使用BeanDefinitionReaderUtils#generateBeanName(BeanDefinition, BeanDefinitionRegistry, boolean) 生成 beanName。
否则使用 XmlReaderContext 对应生成器生成 beanName。
6.根据解析的 BeanDefinition 创建对应的 BeanDefinitionHolder 对象实例。
在解析 bean 配置信息时,主要有如下 七个元数据包装类,对相关属性配置进行归档:
org.springframework.beans.factory.config.ConstructorArgumentValues <constructor-arg>
org.springframework.beans.PropertyValue <property>
org.springframework.beans.factory.support.ManagedArray <array>
org.springframework.beans.factory.support.ManagedList <list>
org.springframework.beans.factory.support.ManagedSet <set>
org.springframework.beans.factory.support.ManagedMap <map>
org.springframework.beans.factory.support.ManagedProperties <props>
每个配置具体字符串值对应了如下三种 处理类型:
org.springframework.beans.factory.config.TypedStringValue <value>
org.springframework.beans.factory.config.RuntimeBeanReference <ref>
org.springframework.beans.factory.config.RuntimeBeanNameReference <idref>
1 /** 2 * Parses the supplied {@code <bean>} element. May return {@code null} 3 * if there were errors during parse. Errors are reported to the 4 * {@link org.springframework.beans.factory.parsing.ProblemReporter}. 5 */ 6 @Nullable 7 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { 8 String id = ele.getAttribute(ID_ATTRIBUTE); 9 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); 10 11 List<String> aliases = new ArrayList<>(); 12 if (StringUtils.hasLength(nameAttr)) { 13 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); 14 aliases.addAll(Arrays.asList(nameArr)); 15 } 16 17 String beanName = id; 18 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { 19 beanName = aliases.remove(0); 20 if (logger.isTraceEnabled()) { 21 logger.trace("No XML 'id' specified - using '" + beanName + 22 "' as bean name and " + aliases + " as aliases"); 23 } 24 } 25 26 if (containingBean == null) { 27 checkNameUniqueness(beanName, aliases, ele); 28 } 29 30 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); 31 if (beanDefinition != null) { 32 if (!StringUtils.hasText(beanName)) { 33 try { 34 if (containingBean != null) { 35 beanName = BeanDefinitionReaderUtils.generateBeanName( 36 beanDefinition, this.readerContext.getRegistry(), true); 37 } 38 else { 39 beanName = this.readerContext.generateBeanName(beanDefinition); 40 // Register an alias for the plain bean class name, if still possible, 41 // if the generator returned the class name plus a suffix. 42 // This is expected for Spring 1.2/2.0 backwards compatibility. 43 String beanClassName = beanDefinition.getBeanClassName(); 44 if (beanClassName != null && 45 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && 46 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { 47 aliases.add(beanClassName); 48 } 49 } 50 if (logger.isTraceEnabled()) { 51 logger.trace("Neither XML 'id' nor 'name' specified - " + 52 "using generated bean name [" + beanName + "]"); 53 } 54 } 55 catch (Exception ex) { 56 error(ex.getMessage(), ele); 57 return null; 58 } 59 } 60 String[] aliasesArray = StringUtils.toStringArray(aliases); 61 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); 62 } 63 64 return null; 65 }
七. BeanDefinitionParserDelegate#decorateIfRequired(Node, BeanDefinitionHolder, @Nullable BeanDefinition)解析 bean 节点自定义属性
DefaultBeanDefinitionDocumentReader#processBeanDefinition(Element, BeanDefinitionParserDelegate)
--> BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element)
--> BeanDefinitionParserDelegate#decorateBeanDefinitionIfRequired(Element, BeanDefinitionHolder)
--> BeanDefinitionParserDelegate#decorateBeanDefinitionIfRequired(Element, BeanDefinitionHolder, BeanDefinition)
--> BeanDefinitionParserDelegate#decorateIfRequired(Node, BeanDefinitionHolder, @Nullable BeanDefinition)
1. 获得节点(Node) 的命名空间URI(NamespaceURI)。
2. 如果命名空间URI不是默认的,则进行特殊处理。
3. 通过 命名空间URI 到读取文件上下文(XmlReaderContext) 中查找对应 处理器(NamespaceHandler)。
例如
Context的处理器: org.springframework.context.config.ContextNamespaceHandler
AOP的处理器: org.springframework.aop.config.AopNamespaceHandler
component的处理器: org.springframework.beans.factory.xml.ComponentNamespaceHandler
4. 调用 NamespaceHandler#decorate(Node, BeanDefinitionHolder, ParserContext)方法解析 节点配置信息,并返回新的 BeanDefinitionHolder 实例。
1 /** 2 * Decorate the given bean definition through a namespace handler, 3 * if applicable. 4 * @param node the current child node 5 * @param originalDef the current bean definition 6 * @param containingBd the containing bean definition (if any) 7 * @return the decorated bean definition 8 */ 9 public BeanDefinitionHolder decorateIfRequired( 10 Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) { 11 12 String namespaceUri = getNamespaceURI(node); 13 if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) { 14 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 15 if (handler != null) { 16 BeanDefinitionHolder decorated = 17 handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd)); 18 if (decorated != null) { 19 return decorated; 20 } 21 } 22 else if (namespaceUri.startsWith("http://www.springframework.org/schema/")) { 23 error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node); 24 } 25 else { 26 // A custom namespace, not to be handled by Spring - maybe "xml:...". 27 if (logger.isDebugEnabled()) { 28 logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]"); 29 } 30 } 31 } 32 return originalDef; 33 }
八. BeanDefinitionReaderUtils#registerBeanDefinition(BeanDefinitionHolder, BeanDefinitionRegistry) 向 BeanFactory 注册 BeanDefinition 实例信息
1. 通过 beanName 向 beanFactory 中注册 BeanDefinition 实例。
2. 通过 beanName 向 beanFactory 中注册 BeanDefinition 实例的别名列表。
1 /** 2 * Register the given bean definition with the given bean factory. 3 * @param definitionHolder the bean definition including name and aliases 4 * @param registry the bean factory to register with 5 * @throws BeanDefinitionStoreException if registration failed 6 */ 7 public static void registerBeanDefinition( 8 BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) 9 throws BeanDefinitionStoreException { 10 11 // Register bean definition under primary name. 12 String beanName = definitionHolder.getBeanName(); 13 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); 14 15 // Register aliases for bean name, if any. 16 String[] aliases = definitionHolder.getAliases(); 17 if (aliases != null) { 18 for (String alias : aliases) { 19 registry.registerAlias(beanName, alias); 20 } 21 } 22 }