开篇
- 承上启下,回顾上一篇文章内容,以及遗留问题
- spring如何加载xml中的各种标签以及如何获取标签中的属性值
- BeanDefinition如何注册到spring容器中(待更新)
一、简单回顾一下spring初级容器XmlBeanFactory
在之前的笔记中,spring初级容器XmlBeanFactory初始化,我们已经了解,spring初级容器XmlBeanFactory在初始化的时候
- 首先,在构造XmlBeanFactory容器时,会将applicationContext.xml配置文件封装成Resource,然后将Resouce资源作为XmlBeanFactory的构造方法参数,创建XmlBeanFactory
- 在XmlBeanFactory构造方法中,首先会添加忽略感知接口,然后将applicationContext.xml文件中的标签封装成Document对象,解析Document中的bean,然后将解析的bean的注入到spring容器中
- 今天接着分析,spring是如何解析applicationContext.xml中的标签,如何将解析的bean注入到spring容器中
二、spring如何解析xml文件中的各种标签和属性值
- 首先,接着上一篇文章spring初级容器XmlBeanFactory初始化的最后,doLoadBeanDefinitions(InputSource inputSource, Resource resource)方法
/**
* Actually load bean definitions from the specified XML file.
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
* @see #doLoadDocument
* @see #registerBeanDefinitions
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//将传进来的inputSource和resource,封装成Document对象
Document doc = doLoadDocument(inputSource, resource);
//解析document对象,并将解析的bean注入到spring中
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);
}
}
可以看到,会将封装好的inputSource和资源Resurce作为参数,传递到方法doLoadDocument(inputSource, resource);方法中,其实就是解析资源,然后封装成Document对象
/**
* Actually load the specified document using the configured DocumentLoader.
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
* @return the DOM Document
* @throws Exception when thrown from the DocumentLoader
* @see #setDocumentLoader
* @see DocumentLoader#loadDocument
*/
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
这里可以看到,将在资源的组件由原来的XmlBeanDefinitionReader递交给DocumentLoader,该组件组要是用来加载Document的,如下
DefaultCocumentLoader
继续看下loadDocument
/**
* Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
* XML parser.
*/
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
可以看到,spring通过Doc来解析xml文件,那spring是如何加载xml文件的呢?我们先回到上面的doLoadDocument方法中
entityResolver解析器在方法loadDocument方法中,传递进来的EntityResolver其实就是在这里通过getEntityResolver()方法获取的,我们来看下getEntityResolver方法
/**
* Return the EntityResolver to use, building a default resolver
* if none specified.
*/
protected EntityResolver getEntityResolver() {
if (this.entityResolver == null) {
// Determine default EntityResolver to use.
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader != null) {
this.entityResolver = new ResourceEntityResolver(resourceLoader);
}
else {
this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
}
}
return this.entityResolver;
}
可以看到,具体是使用ResourceEntityResolver还是DelegatingEntityResolver取决于getResourceLoader()方法返回的是否为非空
@Override
@Nullable
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
看到这里,我们就需要继续跟踪一下,resourceLoader是在哪里进行初始化的,在AbstractBeanDefinitionReader构造方法中,如下
/**
* Create a new AbstractBeanDefinitionReader for the given bean factory.
* <p>If the passed-in bean factory does not only implement the BeanDefinitionRegistry
* interface but also the ResourceLoader interface, it will be used as default
* ResourceLoader as well. This will usually be the case for
* {@link org.springframework.context.ApplicationContext} implementations.
* <p>If given a plain BeanDefinitionRegistry, the default ResourceLoader will be a
* {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
* <p>If the passed-in bean factory also implements {@link EnvironmentCapable} its
* environment will be used by this reader. Otherwise, the reader will initialize and
* use a {@link StandardEnvironment}. All ApplicationContext implementations are
* EnvironmentCapable, while normal BeanFactory implementations are not.
* @param registry the BeanFactory to load bean definitions into,
* in the form of a BeanDefinitionRegistry
* @see #setResourceLoader
* @see #setEnvironment
*/
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
// Determine ResourceLoader to use.
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();
}
}
这里,我们需要知道registry的类型来确认resourceLoader的类型,通过类继承关系可以知道,AbstractBeanDefinitionReader是XmlBeanDefinitionReader的父类,而XmlBeanDefinitionReader是在哪里进行初始化的,还记得吗?
XmlBeanFactory中的XmlBeanDefinitionReader初始化
我们回到XmlBeanFactory类中,我们通过XmlBeanFactory类中的XmlBeanDefinitionReader的构造方法中,可以一步步的走到父类AbstractBeanDefinitionReader的构造方法中。
image.png
image.png
image.png
DTD和XSD
在spring的xml配置文件的开头中
applicationContext.xml
- XSD翻译成英文就是XML Schemas Definition,也就是XML模式的定义,通过XSD的声明文件可以约束我们在xml文件中不能随便乱写,以保证xml文件格式的正确性。
- 除了XSD之外,Spring还支持另外一种约束语言,也就是DTD,DTD翻译英文就是Document Type Definition,也就是文档类型的定义。
在xml文件中,都可以通过 xsi:schemaLocation中的网址进行查看,具体的网址和规范
spring 如何解析DTD和XSD
我们现在回到上面的getEntityResolver()方法中
/**
* Return the EntityResolver to use, building a default resolver
* if none specified.
*/
protected EntityResolver getEntityResolver() {
if (this.entityResolver == null) {
// Determine default EntityResolver to use.
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader != null) {
this.entityResolver = new ResourceEntityResolver(resourceLoader);
}
else {
this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
}
}
return this.entityResolver;
}
因为resourceLoader不为空,所以,entityResolver为:ResourceEntityResolver,我们接着看下ResourceEntityResolver构造方法中,是如何构造ResourceEntityResolver的。
DelegatingEntityResolver.png
BeansDtdResolver就是用来获取DTD声明文件的解析器,而PluggableSchemaResolver是用来获取XSD声明文件的解析器
我们先到BeansDtdResolver中看下,如何解析dtd文件的
BeansDtdResolver解析
我们可以看到,resolverEntity就是解析DTD文件的方法
该方法中有两个参数,分别是publicId和systemId,这两个参数其实就是我们在applicationContext.xml文件中的配置
dtd
通过ClassPathResourc类,从claspath下路径下加载spring-beans.dtd文件,并且将publicid和systemid封装到InputSource中
在创建ClassPathResource类时,将getClass(),也就是BeansDtdResolver传入进来了,这样的话,会在BeansDtdResolver所在classpath寻找spring-beans.dtd,我们再BeansDtdResolver所在的classpath去看下,如下:
spring-beans.dtd
DTD解析完成后,我们再来看下XSD如何解析,我们先到PluggableSchemaResolver中的resolveEntity方法中看下如何解析的
PluggableSchemaResolver
getScheaMappings方法
- systemId肯定为非空,此时调用getSchemaMappings()方法,通过systemId获取资源resourceLocation的位置
那么,我们现在去getSchemaMappings方法中看下
schemaMappingsLocation
- 刚开始,成员变量schemaMappings肯定为空
- 然后通过PropertiesLoaderUtils.loadAllProperties方法加载schemaMappingsLocation中的所有属性
- schemaMappingsLocation具体信息如下:
spring.schemas
- 默认情况下,在PluggableSchemaResolver构造方法中,已经将"META-INF/spring.schemas"赋值给变量schemaMappingsLocation
- 那么,我们就去spring-beans模块下,找到META-INF/spring.schemas
- 由此可见,在spring.schemas文件中,存放的key就是systemId,存放的value为XSD声明文件在项目中的路径
- 我们再回到getSchemaMappings方法中,再回过头来看下,就很简单了,其实就是将spring.schemas文件中的所有封装成一个Map返回回去,然后通过systemId找到XSD文件在项目中路径去获取XSD文件
到此,DTD和XSD声明文件都是通过EntityResolver响应的实现类,已经完成,但是,离我们实际的如何校验、解析xml文件中的bean还很远,但是别急,我们先把校验和解析xml分析万,再来看看
我们再回到doLoadDocument方法中
doLoadDocument
这里可以看到,loadDocument方法,不仅传入了刚刚我们分析的getEntityResolver()方法返回的声明文件解析器,还传入了getValidationModeForResource,获取当前xml文件的校验类型
/**
* Determine the validation mode for the specified {@link Resource}.
* If no explicit validation mode has been configured, then the validation
* mode gets {@link #detectValidationMode detected} from the given resource.
* <p>Override this method if you would like full control over the validation
* mode, even when something other than {@link #VALIDATION_AUTO} was set.
* @see #detectValidationMode
*/
protected int getValidationModeForResource(Resource resource) {
//1.默认获取校验类型为VALIDATION_AUTO
int validationModeToUse = getValidationMode();
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
//2.自动检测校验模式
int detectedMode = detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
// Hmm, we didn't get a clear indication... Let's assume XSD,
// since apparently no DTD declaration has been found up until
// detection stopped (before finding the document's root tag).
return VALIDATION_XSD;
}
默认校验类型如下
image.png
image.png
可以看到,第一个if条件不成立,那么我们到detectValidationMode自动检测中看下
/**
* Detect the validation mode for the XML document in the supplied {@link InputStream}.
* Note that the supplied {@link InputStream} is closed by this method before returning.
* @param inputStream the InputStream to parse
* @throws IOException in case of I/O failure
* @see #VALIDATION_DTD
* @see #VALIDATION_XSD
*/
public int detectValidationMode(InputStream inputStream) throws IOException {
// Peek into the file to look for DOCTYPE.
//1.将输入流inputStream包装成一个缓冲字符输入流,方便读取InputStream
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
try {
boolean isDtdValidated = false;
String content;
while ((content = reader.readLine()) != null) {
content = consumeCommentTokens(content);
if (this.inComment || !StringUtils.hasText(content)) {
continue;
}
//2.内容当中,是否包含"DOCTYPE",什么意思呢,也就是说,如果检测到xml文件中有DOCTYPE字符串
// 就认为它是DTD文件,否则就是XSD文件
if (hasDoctype(content)) {
isDtdValidated = true;
break;
}
if (hasOpeningTag(content)) {
// End of meaningful data...
break;
}
}
return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
}
catch (CharConversionException ex) {
// Choked on some character encoding...
// Leave the decision up to the caller.
return VALIDATION_AUTO;
}
finally {
reader.close();
}
}
这样,spring就可以根据具体的解析类型,分别使用不同的解析器去获取响应的校验文件,这样xml文件在解析时,至少对xml文件的基本格式和规范做了一定的保障
我们还看到,XSD和DTD对应的解析器EntityResolver,根据不同的类型,在jar包中加载对应的声明文件,这样的好处就是防止网络问题,在网上下载声明文件时,因为网络问题而对项目产生影响
加载标签
- 我们再次回到doLoadBeanDefinition的源码位置
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//将传进来的inputSource和resource,封装成Document对象
Document doc = doLoadDocument(inputSource, resource);
//解析document对象,并将解析的bean注入到spring中
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
// 省略部分代码
在校验完xml文件之后,紧接着调用registerBeanDefinitions方法进行xml文件的解析和注册。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//1.通过反射,创建对象BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//2.获取spring容器中,已经注册的bean的数量
int countBefore = getRegistry().getBeanDefinitionCount();
//3.通过documentReader解析Document,并将解析的bean注入到spring容器中
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//4.获取本次注册spring容器中的数量,spring容器中的bean总数量-本次注册前spring容器中bean数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
我们到createBeanDefinitionDocumentReader中看下
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanUtils.instantiateClass(this.documentReaderClass);
}
我们再到documentReaderClass中看下,documentReaderClass是BeanDefinitionDocumentReader
documentReaderClass
创建玩BeanDefinitionDocumentReader对象之后,开始计算当前spring容器中bean的数量,方便我们完成本次注册之后,统计本次注册bean的数量
然后再通过documentReader.registerBeanDefinitions进行解析Document
/**
* This implementation parses bean definitions according to the "spring-beans" XSD
* (or DTD, historically).
* <p>Opens a DOM Document; then initializes the default settings
* specified at the {@code <beans/>} level; then parses the contained bean definitions.
*/
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
这里通过doc.getDocumentElement()读取文档中的元素,获取applicationContext.xml文件中整个根标签,将其传入到doRegisterBeanDefinitions方法中
我们到doRegisterBeanDefinitions方法中
/**
* Register each bean definition within the given root {@code <beans/>} element.
*/
@SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...)
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// 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);
this.delegate = parent;
}
protected BeanDefinitionParserDelegate createDelegate(
XmlReaderContext readerContext, Element root, @Nullable BeanDefinitionParserDelegate parentDelegate) {
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
delegate.initDefaults(root, parentDelegate);
return delegate;
}
BeanDefinitionParserDelegate就是Document封装BeanDefinition的一个代理类
我们到preProcessXml(root);parseBeanDefinitions(root, this.delegate);postProcessXml(root);这三个方法中看下
protected void preProcessXml(Element root) {
}
/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
* @param root the DOM root element of the document
*/
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);
}
}
protected void postProcessXml(Element root) {
}
- preProcessXml和postProcessXml都是空实现,因此,我们可以猜测,这两个方法是留给DefaultBeanDefinitionDocumentReader的子类去扩展的
- 所以,解析xml的逻辑就在parseBeanDefinitions方法中
可以看到,解析标签的工作完全交给了BeanDefinitionParserDelegate
/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
* @param root the DOM root element of the document
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//1.判断当前标签是否是默认标签
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);
}
}
这里首先是判断当前标签是不是默认标签,
判断是否是默认标签
这里判断是否是默认标签还是比较简单,如果namespaceUri为空或者namespaceUri为http://www.springframework.org/schema/beans
否则的话,就为自定义标签,其实,spring中有很多自己自定义的标签,如:<tx:annotation-driven/>,context:component-scan />
那么spring是如何解析默认标签的呢,我们继续到parseDefaultElement中
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
我们首先来看下DefaultBeanDefinitionDocumentReader中的几个常量
DefaultBeanDefinitionDocumentReader中的常量
BeanDefinitionParserDelegate中的常量
BeanDefinitionParserDelegate中的常量
可以看到,这些就是标签的名称,比如标签bean、alias、import、beans。
我们重点来分析下bean标签,因为这个标签也是我们最常用的标签,没有之一
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 1.解析bean标签元素
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 将解析到的bean注册到spring容器当中
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
我们可以看到,这里还是委托BeanDefinitionParserDelegate来帮我们解析,直接通过参数传递的形式传递进来
下面我们来看下,BeanDefinitionParserDelegate是如何解析bean标签的
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
//1.解析bean标签中的id和name属性
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//2.将属性name通过","或";"分隔符进行分割,并将数据添加到aliases中
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
// 3.如果属性id为空,那么就取aliases集合中的第一个value的值,作为bean的名称
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 4. 开始解析bean标签,并将解析结果封装为AbstractBeanDefinition
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
// beanName不为空,直接跳过
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
// 5. 将解析的beanDefinition,beanName和aliase,创建一个BeanDefinitionHolder
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
这里的MULTI_VALUE_ATTRIBUTE_DELIMITERS的值为 ",; ",也就是说在配置属性name的值时,可以通过“,”或“;”作为分隔符,配置多个name属性值。如"order,orders",分割之后可以得到["order","orders"]的数组,放到aliases中
在第四步中,parseBeanDefinitionElement(ele, beanName, containingBean);极为关键,解析bean标签并封装成AbstractBeanDefinition
我们来看下解析bean标签并封装成AbstractBeanDefinition
可以看到,还是继续解析bean标签中的属性,分别获取属性class的值className,及属性parent的值parent,然后将这个两个属性的值传入方法createBeanDefinition中,构建一个AbstractBeanDefinition类型的对象bd。
/**
* Parse the bean definition itself, without regard to name or aliases. May return
* {@code null} if problems occurred during the parsing of the bean definition.
*/
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
//1.如果标签中存在class属性,那么就获取class属性值
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
//2.如果标签中存在parent标签,那么就获取parent标签的属性值
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
//3.通过属性class和parent,初步创建AbstractBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//4.解析bean标签中的各种属性,并封装到AbstractBeanDefinition中
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 5.解析标签中各种子标签元素,并将解析结果封装到AbstractBeanDefinition中
// 解析bean标签中的meta
parseMetaElements(ele, bd);
// 解析bean标签中的lookup-method子标签元素
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析bean标签中replace-method子标签元素
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析bean的子标签constructor-arg
parseConstructorArgElements(ele, bd);
//解析bean的子标签property
parsePropertyElements(ele, bd);
//解析bean的子标签qualifier
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
//6.返回bd
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
在上面的parseBeanDefinitionElement方法的第三步中,创建BeanDefinition,我们来看下
/**
* Create a bean definition for the given class name and parent name.
* @param className the name of the bean class
* @param parentName the name of the bean's parent bean
* @return the newly created bean definition
* @throws ClassNotFoundException if bean class resolution was attempted but failed
*/
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}
/**
* Create a new GenericBeanDefinition for the given parent name and class name,
* eagerly loading the bean class if a ClassLoader has been specified.
* @param parentName the name of the parent bean, if any
* @param className the name of the bean class, if any
* @param classLoader the ClassLoader to use for loading bean classes
* (can be {@code null} to just register bean classes by name)
* @return the bean definition
* @throws ClassNotFoundException if the bean class could not be loaded
*/
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
可以看到,实际上是创建的BeanDefinition为GenericBeanDefinition,并且将parent和class属性的值都设置到GenericBeanDefinition之后并返回。
到此,我们可以知道,bean在spring容器中都是以BeanDefinition的形式存在,而BeanDefinition只是一个接口,因此。Spring在解析bean标签时会为我们创建一个GenericBeanDefinition出来,用于存放bean标签解析出来的各种信息,所以,接下来我们有必要来了解下什么是GenericBeanDefinition。
BeanDefinition
首先来 看下BeanDefinition中有哪些东西
1.在spring中,每个对象都称之为bean,而bean是以BeanDefinition的形式存在
2.既然bean是以BeanDefinition形式存在,那BeanDefinition就是用来存放bean的基本信息,其实就是bean的定义
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
/**
* Scope identifier for the standard singleton scope: {@value}.
* <p>Note that extended bean factories might support further scopes.
* @see #setScope
* @see ConfigurableBeanFactory#SCOPE_SINGLETON
*/
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
/**
* Scope identifier for the standard prototype scope: {@value}.
* <p>Note that extended bean factories might support further scopes.
* @see #setScope
* @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
*/
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
/**
* Role hint indicating that a {@code BeanDefinition} is a major part
* of the application. Typically corresponds to a user-defined bean.
*/
int ROLE_APPLICATION = 0;
//省略部分代码.....
既然BeanDefinition是一个接口,我们先来看看,BeanDefinition继承关系
BeanDefinition继承关系
可以看到,BeanDefinition接口的实现类为抽象类AbstractBeanDefinition,而AbstractBeanDefinition的实现类有三个,分别是ChildBeanDefinition、RootBeanDefinition、GenericBeanDefinition,其中,GenericBeanDefinition就是我们上面源码中初始化实现的BeanDefinition实现类
bean标签经过解析后,在spring容器中,刚开始是通过RootBeanDefinition,就比如,我们在applicationContext.xml文件中的配置
bean标签
因为bean标签中没有设置parent属性的值,也就是说它没有指定自己的父bean,所以可以使用RootBeanDefinition来封装该标签的信息,表示存放的是bean标签的根节点信息。
在RootBeanDefinition中有setParentName方法,如果parentName不为空,则会报错,所以,RootBeanDefinition在封装bean信息时,是不允许有父亲的。
RootBeanDefinition
根据上面的BeanDefinition继承关系,再来看下ChildBeanDefinition和RootBeanDefinition
ChildBeanDefinition
1.通过注释我们可以知道,从spring2.5之后,就会推荐使用GenericBeanDefinition方式进行注册bean
2.GenericBeanDefinition可以使用setParentName的方式,设置父bean的依赖
而AnnotatedGenericBeanDefinition其实简单来说,就是用来封装我们通过注解扫描来的bean,比如@Bean,@Service,@Component,@Repository
AbstractBeanDefinition作用BeanDefinition的抽象类,里面封装了很多公共的属性。如下:
AbstractBeanDefinition
看完BeanDefinition继承关系之后,我们接着来看parseBeanDefinitionElement中的方法,parseBeanDefinitionAttributes解析bean中的属性
/**
* Apply the attributes of the given bean element to the given bean * definition.
* @param ele bean declaration element
* @param beanName bean name
* @param containingBean containing bean definition
* @return a bean definition initialized according to the bean element attributes
*/
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
//获取属性singleton的值
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}
else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
bd.setScope(containingBean.getScope());
}
//获取属性abstract的值
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
//获取属性lazy-init
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
//获取属性autowire的值
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
//获取属性depends-on的值
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
//获取属性autowire-candidate的值
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if (isDefaultValue(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
}
else {
bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
}
// 获取属性primary的值
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
//获取属性init-method的值
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
bd.setInitMethodName(initMethodName);
}
else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
//获取属性destroy-method
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
}
else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
//获取属性factory-method的值
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}
解析完属性之后,接着就会解析子标签,meta、lookup-method、replaced-method、constructor-args、property和qualifier,其中我们比较熟悉的还是标签constructor-args和property
解析标签的逻辑大同小异,我们来看下是如何解析meta子标签的逻辑
/**
* Parse the meta elements underneath the given element, if any.
*/
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
//获取bean标签下的所有子标签
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//遍历找到meta标签
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element) node;
//获取属性key的值
String key = metaElement.getAttribute(KEY_ATTRIBUTE);
//获取属性value的值
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
//将key和value的值封装到BeanMetadataAttribute
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
attribute.setSource(extractSource(metaElement));
//将BeanMetadataAttribute添加到BeanMetadataAttributeAccessor
attributeAccessor.addMetadataAttribute(attribute);
}
}
}
可以看到,解析过程也是相当的清晰简单,就是遍历bean标签,然后找到meta标签。将解析到的mata标签下的key和value的值,封装到BeanMetadataAttribute,然后将BeanMetadataAttribute添加到BeanMetadataAttributeAccessor
AbstractBeanDefinition继承了BeanMetadataAttributeAccessor,BeanMetadataAtrribute和BeanMetadataAttributeAccessor,我们可以理解为是Bean封装属性和访问属性的底层类
parseLookupOverrideSubElements
/**
* Parse lookup-override sub-elements of the given bean element.
*/
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
//获取bean标签下的所有子标签
NodeList nl = beanEle.getChildNodes();
//遍历所有的子标签
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//在子标签中找到lookup-method标签
if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
Element ele = (Element) node;
//获取lookup-method标签的name属性值
String methodName = ele.getAttribute(NAME_ATTRIBUTE);
//获取属性bean的值
String beanRef = ele.getAttribute(BEAN_ELEMENT);
//将methodName和beanRef封装成LookupOverride
LookupOverride override = new LookupOverride(methodName, beanRef);
override.setSource(extractSource(ele));
//黄LookupOverride添加到MethodOverrides
overrides.addOverride(override);
}
}
}
parseReplacedMethodSubElements
/**
* Parse replaced-method sub-elements of the given bean element.
*/
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
//获取bean标签下的所有子标签
NodeList nl = beanEle.getChildNodes();
//遍历子标签
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//找到子标签是replaced-method
if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
Element replacedMethodEle = (Element) node;
//获取name的属性值
String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
//获取replacer的属性值
String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
//将name的属性值和replacer的属性值封装成ReplaceOverride
ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
// Look for arg-type match elements.
//进一步看下replaced-method标签下的arg-type子标签
List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
for (Element argTypeEle : argTypeEles) {
//获取子标签arg-type下的match的属性值
String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
if (StringUtils.hasText(match)) {
//将属性match的值,一起封装的replaceOverride
replaceOverride.addTypeIdentifier(match);
}
}
replaceOverride.setSource(extractSource(replacedMethodEle));
//将replaceOverride添加到MethodOverrides
overrides.addOverride(replaceOverride);
}
}
}
对于上面的三个标签,mata,lookup-method和replace-method,我们在实际开发过程中,几乎是用不到的,不比过多的去关注,如果好奇具体用法,可以自行搜索下。
对于bean的子标签,像constructor-arg,property和qualifier,我们实际用到的场景会比较多一些
我们来看下解析constructor-arg标签
/**
* Parse constructor-arg sub-elements of the given bean element.
*/
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
//获取bean标签下的所有子标签
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//遍历找到constructor-arg标签
if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
//解析constructor-arg标签
parseConstructorArgElement((Element) node, bd);
}
}
}
/**
* Parse a constructor-arg element.
*/
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
//获取index的属性值
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
//获取type的属性值
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
//获取name的属性值
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//处理index存在的情况下
if (StringUtils.hasLength(indexAttr)) {
try {
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
}
else {
try {
this.parseState.push(new ConstructorArgumentEntry(index));
//解析constructor-arg标签下的所有属性值
Object value = parsePropertyValue(ele, bd, null);
//将constructor-arg解析的所有属性值封装到ValueHolder中
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
//index的属性值不能重复,否则会混淆
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
error("Ambiguous constructor-arg entries for index " + index, ele);
}
else {
//将constructor-arg标签解析到的所有信息,都封装在BeanDefinition
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
}
}
finally {
this.parseState.pop();
}
}
}
catch (NumberFormatException ex) {
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
}
}
//处理属性name存在的情况
else {
try {
this.parseState.push(new ConstructorArgumentEntry());
//解析constructor-arg标签下的所有属性值
Object value = parsePropertyValue(ele, bd, null);
//将解析到的所有属性值,封装到ValueHolder中
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
//将constructor-arg标签解析到的所有信息,都封装在BeanDefinition
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
- 可以看到,首先是看标签中的属性是index还是name,如果是index,就走if分支,否则就走else分支
2.最后会把解析到的标签的属性值,封装到BeanDefinition中
我们来看下到底是如何具体解析constructor-arg标签的
/**
* Get the value of a property element. May be a list etc.
* Also used for constructor arguments, "propertyName" being null in this case.
*/
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// Should only have one child element: ref, value, list, etc.
//获取当前节点的所有子标签
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//不处理description和meta标签
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
// Child element is what we're looking for.
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {
//记录子标签
subElement = (Element) node;
}
}
}
//是否包含ref属性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
//是否包含value属性
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
//ref和value不能同时存在
//或者是ref和value只存在一个,但是不能再有子标签
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
// 如果ref标签存在
if (hasRefAttribute) {
//获取标签的属性值
String refName = ele.getAttribute(REF_ATTRIBUTE);
//ref属性存在,但是值不能为空
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
//value属性的处理
else if (hasValueAttribute) {
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
else if (subElement != null) {
//解析ele下面的所有子标签
return parsePropertySubElement(subElement, bd);
}
else {
// Neither child element nor "ref" or "value" attribute found.
//ref和value的属性不能都不设置
error(elementName + " must specify a ref or value", ele);
return null;
}
}
1.首先是将description和meta标签剔除掉不处理,如果还存在子标签的话,就记录到subElement(如:array,list,set标签)
2.属性ref和value不能同时存在,因为ref代表引用另外一个bean,而value是代表某一个具体的值,所以只能二选一
3.如果ref和value都没有配置,而且也没有配置子标签的话,这样也是不行的,直接报错
4.如果子标签错在的话,最后会在else if (subElement != null) 中,解析子标签
parsePropertySubElement解析子标签
/**
* Parse a value, ref or collection sub-element of a property or
* constructor-arg element.
* @param ele subelement of property element; we don't know which yet
* @param bd the current bean definition (if any)
*/
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
}
/**
* Parse a value, ref or collection sub-element of a property or
* constructor-arg element.
* @param ele subelement of property element; we don't know which yet
* @param bd the current bean definition (if any)
* @param defaultValueType the default type (class name) for any
* {@code <value>} tag that might be created
*/
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
//处理便签bean
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
//处理ref标签
else if (nodeNameEquals(ele, REF_ELEMENT)) {
// A generic reference to any name of any bean.
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in a parent context.
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean' or 'parent' is required for <ref> element", ele);
return null;
}
}
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
ref.setSource(extractSource(ele));
return ref;
}
//处理idref标签
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
//处理value标签
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
//处理null标签
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
// It's a distinguished null value. Let's wrap it in a TypedStringValue
// object in order to preserve the source location.
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
//处理array标签
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
//处理list标签
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
//处理set标签
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
//处理map标签
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
//处理props标签
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
其实对于property标签的处理结果也是类似
网友评论