開篇
- 承上啟下墨叛,回顧上一篇文章內(nèi)容衣吠,以及遺留問題
- spring如何加載xml中的各種標(biāo)簽以及如何獲取標(biāo)簽中的屬性值
- BeanDefinition如何注冊到spring容器中(待更新)
一、簡單回顧一下spring初級容器XmlBeanFactory
在之前的筆記中,spring初級容器XmlBeanFactory初始化,我們已經(jīng)了解汤踏,spring初級容器XmlBeanFactory在初始化的時候
- 首先,在構(gòu)造XmlBeanFactory容器時,會將applicationContext.xml配置文件封裝成Resource诬烹,然后將Resouce資源作為XmlBeanFactory的構(gòu)造方法參數(shù),創(chuàng)建XmlBeanFactory
- 在XmlBeanFactory構(gòu)造方法中弃鸦,首先會添加忽略感知接口绞吁,然后將applicationContext.xml文件中的標(biāo)簽封裝成Document對象,解析Document中的bean寡键,然后將解析的bean的注入到spring容器中
- 今天接著分析掀泳,spring是如何解析applicationContext.xml中的標(biāo)簽,如何將解析的bean注入到spring容器中
二西轩、spring如何解析xml文件中的各種標(biāo)簽和屬性值
- 首先员舵,接著上一篇文章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作為參數(shù)注服,傳遞到方法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的瞭郑,如下
繼續(xù)看下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方法中
在方法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;
}
看到這里,我們就需要繼續(xù)跟蹤一下愉老,resourceLoader是在哪里進行初始化的,在AbstractBeanDefinitionReader構(gòu)造方法中场绿,如下
/**
* 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的類型嫉入,通過類繼承關(guān)系可以知道焰盗,AbstractBeanDefinitionReader是XmlBeanDefinitionReader的父類,而XmlBeanDefinitionReader是在哪里進行初始化的劝贸,還記得嗎姨谷?
我們回到XmlBeanFactory類中,我們通過XmlBeanFactory類中的XmlBeanDefinitionReader的構(gòu)造方法中映九,可以一步步的走到父類AbstractBeanDefinitionReader的構(gòu)造方法中梦湘。
DTD和XSD
在spring的xml配置文件的開頭中
- XSD翻譯成英文就是XML Schemas Definition,也就是XML模式的定義件甥,通過XSD的聲明文件可以約束我們在xml文件中不能隨便亂寫捌议,以保證xml文件格式的正確性。
- 除了XSD之外引有,Spring還支持另外一種約束語言瓣颅,也就是DTD,DTD翻譯英文就是Document Type Definition譬正,也就是文檔類型的定義宫补。
在xml文件中,都可以通過 xsi:schemaLocation中的網(wǎng)址進行查看曾我,具體的網(wǎng)址和規(guī)范
spring 如何解析DTD和XSD
我們現(xiàn)在回到上面的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構(gòu)造方法中抒巢,是如何構(gòu)造ResourceEntityResolver的贫贝。
BeansDtdResolver就是用來獲取DTD聲明文件的解析器,而PluggableSchemaResolver是用來獲取XSD聲明文件的解析器
我們先到BeansDtdResolver中看下,如何解析dtd文件的
我們可以看到稚晚,resolverEntity就是解析DTD文件的方法
該方法中有兩個參數(shù)崇堵,分別是publicId和systemId,這兩個參數(shù)其實就是我們在applicationContext.xml文件中的配置
dtd
通過ClassPathResourc類,從claspath下路徑下加載spring-beans.dtd文件客燕,并且將publicid和systemid封裝到InputSource中
在創(chuàng)建ClassPathResource類時鸳劳,將getClass(),也就是BeansDtdResolver傳入進來了,這樣的話幸逆,會在BeansDtdResolver所在classpath尋找spring-beans.dtd棍辕,我們再BeansDtdResolver所在的classpath去看下暮现,如下:
spring-beans.dtd
DTD解析完成后还绘,我們再來看下XSD如何解析,我們先到PluggableSchemaResolver中的resolveEntity方法中看下如何解析的
- systemId肯定為非空栖袋,此時調(diào)用getSchemaMappings()方法拍顷,通過systemId獲取資源resourceLocation的位置
那么,我們現(xiàn)在去getSchemaMappings方法中看下
- 剛開始塘幅,成員變量schemaMappings肯定為空
- 然后通過PropertiesLoaderUtils.loadAllProperties方法加載schemaMappingsLocation中的所有屬性
- schemaMappingsLocation具體信息如下:
- 默認情況下昔案,在PluggableSchemaResolver構(gòu)造方法中,已經(jīng)將"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響應(yīng)的實現(xiàn)類咧七,已經(jīng)完成衰齐,但是,離我們實際的如何校驗继阻、解析xml文件中的bean還很遠耻涛,但是別急,我們先把校驗和解析xml分析萬瘟檩,再來看看
我們再回到doLoadDocument方法中
這里可以看到抹缕,loadDocument方法,不僅傳入了剛剛我們分析的getEntityResolver()方法返回的聲明文件解析器芒帕,還傳入了getValidationModeForResource歉嗓,獲取當(dāng)前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.自動檢測校驗?zāi)J? 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;
}
默認校驗類型如下
可以看到,第一個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.內(nèi)容當(dāng)中鉴分,是否包含"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就可以根據(jù)具體的解析類型伦糯,分別使用不同的解析器去獲取響應(yīng)的校驗文件柜某,這樣xml文件在解析時,至少對xml文件的基本格式和規(guī)范做了一定的保障
我們還看到敛纲,XSD和DTD對應(yīng)的解析器EntityResolver,根據(jù)不同的類型喂击,在jar包中加載對應(yīng)的聲明文件,這樣的好處就是防止網(wǎng)絡(luò)問題淤翔,在網(wǎng)上下載聲明文件時翰绊,因為網(wǎng)絡(luò)問題而對項目產(chǎn)生影響
加載標(biāo)簽
- 我們再次回到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文件之后监嗜,緊接著調(diào)用registerBeanDefinitions方法進行xml文件的解析和注冊。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//1.通過反射抡谐,創(chuàng)建對象BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//2.獲取spring容器中裁奇,已經(jīng)注冊的bean的數(shù)量
int countBefore = getRegistry().getBeanDefinitionCount();
//3.通過documentReader解析Document,并將解析的bean注入到spring容器中
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//4.獲取本次注冊spring容器中的數(shù)量麦撵,spring容器中的bean總數(shù)量-本次注冊前spring容器中bean數(shù)量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
我們到createBeanDefinitionDocumentReader中看下
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanUtils.instantiateClass(this.documentReaderClass);
}
我們再到documentReaderClass中看下,documentReaderClass是BeanDefinitionDocumentReader
創(chuàng)建玩BeanDefinitionDocumentReader對象之后刽肠,開始計算當(dāng)前spring容器中bean的數(shù)量,方便我們完成本次注冊之后厦坛,統(tǒng)計本次注冊bean的數(shù)量
然后再通過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文件中整個根標(biāo)簽,將其傳入到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都是空實現(xiàn)杜秸,因此放仗,我們可以猜測,這兩個方法是留給DefaultBeanDefinitionDocumentReader的子類去擴展的
- 所以撬碟,解析xml的邏輯就在parseBeanDefinitions方法中
可以看到诞挨,解析標(biāo)簽的工作完全交給了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.判斷當(dāng)前標(biāo)簽是否是默認標(biāo)簽
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)) {
//解析默認標(biāo)簽中的元素
parseDefaultElement(ele, delegate);
}
else {
//解析自定義標(biāo)簽中的元素
delegate.parseCustomElement(ele);
}
}
}
}
else {
//解析自定義標(biāo)簽中的元素
delegate.parseCustomElement(root);
}
}
這里首先是判斷當(dāng)前標(biāo)簽是不是默認標(biāo)簽,
這里判斷是否是默認標(biāo)簽還是比較簡單呢蛤,如果namespaceUri為空或者namespaceUri為http://www.springframework.org/schema/beans
否則的話惶傻,就為自定義標(biāo)簽,其實其障,spring中有很多自己自定義的標(biāo)簽银室,如:<tx:annotation-driven/>,context:component-scan />
那么spring是如何解析默認標(biāo)簽的呢,我們繼續(xù)到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中的幾個常量
BeanDefinitionParserDelegate中的常量
可以看到,這些就是標(biāo)簽的名稱蜈敢,比如標(biāo)簽bean辜荠、alias、import抓狭、beans伯病。
我們重點來分析下bean標(biāo)簽,因為這個標(biāo)簽也是我們最常用的標(biāo)簽否过,沒有之一
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 1.解析bean標(biāo)簽元素
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 將解析到的bean注冊到spring容器當(dāng)中
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來幫我們解析,直接通過參數(shù)傳遞的形式傳遞進來
下面我們來看下苗桂,BeanDefinitionParserDelegate是如何解析bean標(biāo)簽的
/**
* 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標(biāo)簽中的id和name屬性
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//2.將屬性name通過","或";"分隔符進行分割药磺,并將數(shù)據(jù)添加到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標(biāo)簽与涡,并將解析結(jié)果封裝為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,創(chuàng)建一個BeanDefinitionHolder
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
這里的MULTI_VALUE_ATTRIBUTE_DELIMITERS的值為 ",; ",也就是說在配置屬性name的值時葱椭,可以通過“廓鞠,”或“;”作為分隔符怎囚,配置多個name屬性值卿叽。如"order,orders",分割之后可以得到["order","orders"]的數(shù)組,放到aliases中
在第四步中恳守,parseBeanDefinitionElement(ele, beanName, containingBean);極為關(guān)鍵考婴,解析bean標(biāo)簽并封裝成AbstractBeanDefinition
我們來看下解析bean標(biāo)簽并封裝成AbstractBeanDefinition
可以看到,還是繼續(xù)解析bean標(biāo)簽中的屬性催烘,分別獲取屬性class的值className沥阱,及屬性parent的值parent,然后將這個兩個屬性的值傳入方法createBeanDefinition中伊群,構(gòu)建一個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.如果標(biāo)簽中存在class屬性,那么就獲取class屬性值
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
//2.如果標(biāo)簽中存在parent標(biāo)簽舰始,那么就獲取parent標(biāo)簽的屬性值
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
//3.通過屬性class和parent崇棠,初步創(chuàng)建AbstractBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//4.解析bean標(biāo)簽中的各種屬性,并封裝到AbstractBeanDefinition中
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 5.解析標(biāo)簽中各種子標(biāo)簽元素丸卷,并將解析結(jié)果封裝到AbstractBeanDefinition中
// 解析bean標(biāo)簽中的meta
parseMetaElements(ele, bd);
// 解析bean標(biāo)簽中的lookup-method子標(biāo)簽元素
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析bean標(biāo)簽中replace-method子標(biāo)簽元素
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析bean的子標(biāo)簽constructor-arg
parseConstructorArgElements(ele, bd);
//解析bean的子標(biāo)簽property
parsePropertyElements(ele, bd);
//解析bean的子標(biāo)簽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方法的第三步中枕稀,創(chuàng)建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;
}
可以看到,實際上是創(chuàng)建的BeanDefinition為GenericBeanDefinition萎坷,并且將parent和class屬性的值都設(shè)置到GenericBeanDefinition之后并返回范抓。
到此,我們可以知道食铐,bean在spring容器中都是以BeanDefinition的形式存在匕垫,而BeanDefinition只是一個接口,因此虐呻。Spring在解析bean標(biāo)簽時會為我們創(chuàng)建一個GenericBeanDefinition出來象泵,用于存放bean標(biāo)簽解析出來的各種信息,所以斟叼,接下來我們有必要來了解下什么是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繼承關(guān)系
可以看到,BeanDefinition接口的實現(xiàn)類為抽象類AbstractBeanDefinition,而AbstractBeanDefinition的實現(xiàn)類有三個识腿,分別是ChildBeanDefinition出革、RootBeanDefinition、GenericBeanDefinition,其中渡讼,GenericBeanDefinition就是我們上面源碼中初始化實現(xiàn)的BeanDefinition實現(xiàn)類
bean標(biāo)簽經(jīng)過解析后骂束,在spring容器中,剛開始是通過RootBeanDefinition,就比如成箫,我們在applicationContext.xml文件中的配置
因為bean標(biāo)簽中沒有設(shè)置parent屬性的值展箱,也就是說它沒有指定自己的父bean,所以可以使用RootBeanDefinition來封裝該標(biāo)簽的信息蹬昌,表示存放的是bean標(biāo)簽的根節(jié)點信息混驰。
在RootBeanDefinition中有setParentName方法,如果parentName不為空凳厢,則會報錯账胧,所以,RootBeanDefinition在封裝bean信息時先紫,是不允許有父親的治泥。
根據(jù)上面的BeanDefinition繼承關(guān)系,再來看下ChildBeanDefinition和RootBeanDefinition
1.通過注釋我們可以知道遮精,從spring2.5之后居夹,就會推薦使用GenericBeanDefinition方式進行注冊bean
2.GenericBeanDefinition可以使用setParentName的方式败潦,設(shè)置父bean的依賴
而AnnotatedGenericBeanDefinition其實簡單來說,就是用來封裝我們通過注解掃描來的bean准脂,比如@Bean,@Service,@Component,@Repository
AbstractBeanDefinition作用BeanDefinition的抽象類劫扒,里面封裝了很多公共的屬性。如下:
看完BeanDefinition繼承關(guān)系之后狸膏,我們接著來看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;
}
解析完屬性之后,接著就會解析子標(biāo)簽湾戳,meta贤旷、lookup-method、replaced-method砾脑、constructor-args幼驶、property和qualifier,其中我們比較熟悉的還是標(biāo)簽constructor-args和property
解析標(biāo)簽的邏輯大同小異韧衣,我們來看下是如何解析meta子標(biāo)簽的邏輯
/**
* Parse the meta elements underneath the given element, if any.
*/
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
//獲取bean標(biāo)簽下的所有子標(biāo)簽
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//遍歷找到meta標(biāo)簽
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);
}
}
}
可以看到盅藻,解析過程也是相當(dāng)?shù)那逦唵危褪潜闅vbean標(biāo)簽畅铭,然后找到meta標(biāo)簽氏淑。將解析到的mata標(biāo)簽下的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標(biāo)簽下的所有子標(biāo)簽
NodeList nl = beanEle.getChildNodes();
//遍歷所有的子標(biāo)簽
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//在子標(biāo)簽中找到lookup-method標(biāo)簽
if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
Element ele = (Element) node;
//獲取lookup-method標(biāo)簽的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標(biāo)簽下的所有子標(biāo)簽
NodeList nl = beanEle.getChildNodes();
//遍歷子標(biāo)簽
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//找到子標(biāo)簽是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標(biāo)簽下的arg-type子標(biāo)簽
List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
for (Element argTypeEle : argTypeEles) {
//獲取子標(biāo)簽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);
}
}
}
對于上面的三個標(biāo)簽,mata,lookup-method和replace-method匀归,我們在實際開發(fā)過程中坑资,幾乎是用不到的,不比過多的去關(guān)注穆端,如果好奇具體用法袱贮,可以自行搜索下。
對于bean的子標(biāo)簽攒巍,像constructor-arg柒莉,property和qualifier沽翔,我們實際用到的場景會比較多一些
我們來看下解析constructor-arg標(biāo)簽
/**
* Parse constructor-arg sub-elements of the given bean element.
*/
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
//獲取bean標(biāo)簽下的所有子標(biāo)簽
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//遍歷找到constructor-arg標(biāo)簽
if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
//解析constructor-arg標(biāo)簽
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標(biāo)簽下的所有屬性值
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的屬性值不能重復(fù),否則會混淆
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
error("Ambiguous constructor-arg entries for index " + index, ele);
}
else {
//將constructor-arg標(biāo)簽解析到的所有信息雳殊,都封裝在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標(biāo)簽下的所有屬性值
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標(biāo)簽解析到的所有信息夯秃,都封裝在BeanDefinition
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
- 可以看到,首先是看標(biāo)簽中的屬性是index還是name痢艺,如果是index仓洼,就走if分支,否則就走else分支
2.最后會把解析到的標(biāo)簽的屬性值腹备,封裝到BeanDefinition中
我們來看下到底是如何具體解析constructor-arg標(biāo)簽的
/**
* 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.
//獲取當(dāng)前節(jié)點的所有子標(biāo)簽
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//不處理description和meta標(biāo)簽
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 {
//記錄子標(biāo)簽
subElement = (Element) node;
}
}
}
//是否包含ref屬性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
//是否包含value屬性
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
//ref和value不能同時存在
//或者是ref和value只存在一個衬潦,但是不能再有子標(biāo)簽
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標(biāo)簽存在
if (hasRefAttribute) {
//獲取標(biāo)簽的屬性值
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下面的所有子標(biāo)簽
return parsePropertySubElement(subElement, bd);
}
else {
// Neither child element nor "ref" or "value" attribute found.
//ref和value的屬性不能都不設(shè)置
error(elementName + " must specify a ref or value", ele);
return null;
}
}
1.首先是將description和meta標(biāo)簽剔除掉不處理植酥,如果還存在子標(biāo)簽的話镀岛,就記錄到subElement(如:array,list,set標(biāo)簽)
2.屬性ref和value不能同時存在,因為ref代表引用另外一個bean友驮,而value是代表某一個具體的值漂羊,所以只能二選一
3.如果ref和value都沒有配置,而且也沒有配置子標(biāo)簽的話卸留,這樣也是不行的走越,直接報錯
4.如果子標(biāo)簽錯在的話,最后會在else if (subElement != null) 中耻瑟,解析子標(biāo)簽
parsePropertySubElement解析子標(biāo)簽
/**
* 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標(biāo)簽
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標(biāo)簽
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
//處理value標(biāo)簽
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
//處理null標(biāo)簽
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標(biāo)簽
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
//處理list標(biāo)簽
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
//處理set標(biāo)簽
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
//處理map標(biāo)簽
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
//處理props標(biāo)簽
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
其實對于property標(biāo)簽的處理結(jié)果也是類似