寫在前面的話
相關背景及資源:
曹工說Spring Boot源碼(1)-- Bean Definition到底是什么,附spring思維導圖分享
曹工說Spring Boot源碼(2)-- Bean Definition到底是什么,咱們對着接口,逐個方法講解
曹工說Spring Boot源碼(3)-- 手動注冊Bean Definition不比游戲好玩嗎,我們來試一下
曹工說Spring Boot源碼(4)-- 我是怎么自定義ApplicationContext,從json文件讀取bean definition的?
曹工說Spring Boot源碼(5)-- 怎么從properties文件讀取bean
曹工說Spring Boot源碼(6)-- Spring怎么從xml文件里解析bean的
工程結構圖:
概要
大家看到這個標題,不知道心里有答案了沒?大家再想想,xml文件里都有什么呢?
這么一想,spring的xml文件里,內容真的很多,估計很多元素你也沒配置過,尤其是這兩年新出來的程序員,估計都在吐槽了,現在不都是注解了嗎,誰還用xml?但其實,不管是xml,還是注解,都是配置信息,只是不同的表現形式而已,看過我前面幾講的同學,應該知道,我們用json、properties文件寫過bean的配置信息。
所以,具體形式不重要,xml和注解只是最常用的兩種表達方式罷了,我們這次就以xml為例來講解。
xml中,其實還是很有條理的,各種元素,都按照namespace分得明明白白的,我列了個表格如下:
namespace | element |
---|---|
util | constant、property-path、list、set、map、properties |
context | property-placeholder、property-override、annotation-config、component-scan、load-time-weaver、spring-configured、mbean-export、mbean-server |
beans | import、bean、alias |
task | annotation-driven、scheduler、scheduled-tasks、executor |
cache | advice、annotation-driven |
aop | config、scoped-proxy、aspectj-autoproxy |
大家看到了嗎,spring其實對xml的支持才是最全面的,注解有的,xml基本都有。作為一個工作了6年的碼農,我發現好多元素我都沒配置過,更別說熟悉其內在原理了。但是呢,我們還是不能忘記了今天的標題,這么多元素,難道沒有什么共性嗎?spring解析這些元素,到底都是怎么實現的呢,且不說這些元素怎么生效,讀了東西總需要地方存起來吧,那,是怎么存放的呢?
我們會挑選一些元素來講解。我們本講,先講解spring采用的xml解析方式;再從util這個namespace開始,挑了constant這個元素進行深入講解。
spring中所采用的xml解析方式
上一講,我們講了,spring是怎么解析xml元素的,我今天想辦法從spring源碼里,把它用來解析xml的主干代碼提取了一下,基本就是下面這樣的,比如針對如下xml文件,我們打算遍歷一遍:
test-xml-read.xml:
<?xml version="1.0" encoding="UTF-8"?>
<f:table xmlns:f="http://www.w3school.com.cn/furniture"
xmlns:t="http://www.w3school.com.cn/t">
<f:name>African Coffee Table</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
<t:abc></t:abc>
</f:table>
那么,spring里的代碼骨架,大概如下:
package org.springframework.bootstrap.sample;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
@Slf4j
public class XmlSimpleUse {
public static void main(String[] args) {
//讀取xml文件
URL url = Thread.currentThread().getContextClassLoader()
.getResource("test-xml-read.xml");
InputStream inputStream = url.openStream();
//將流轉變為InputSource,在后續xml解析使用
InputSource inputSource = new InputSource(inputStream);
DocumentBuilderFactory factory = createDocumentBuilderFactory();
DocumentBuilder docBuilder = factory.newDocumentBuilder();
// 可選,設置實體解析器,其實就是:你可以自定義去哪里加載xsd/dtd文件
docBuilder.setEntityResolver(new EntityResolver() {
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
return null;
}
});
// 設置回調處理器,當解析出現錯誤時,(比如xsd里指定了不能出現a元素,然后xml里出現了a元素)
docBuilder.setErrorHandler(null);
//解析xml文件,獲取到Document,代表了整個文件
Document document = docBuilder.parse(inputSource);
// 獲取根元素
Element root = document.getDocumentElement();
log.info("root is {}",root);
//獲取根元素下的每個child元素
NodeList nodeList = root.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
log.info("ele:{}",ele);
}
}
}
protected static DocumentBuilderFactory createDocumentBuilderFactory() {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(true);
// Enforce namespace aware for XSD...
factory.setNamespaceAware(true);
return factory;
}
}
輸出如下:
21:38:19.638 [main] INFO o.s.bootstrap.sample.XmlSimpleUse - root is [f:table: null]
21:38:19.653 [main] INFO o.s.bootstrap.sample.XmlSimpleUse - ele:[f:name: null]
21:38:19.653 [main] INFO o.s.bootstrap.sample.XmlSimpleUse - ele:[f:width: null]
21:38:19.653 [main] INFO o.s.bootstrap.sample.XmlSimpleUse - ele:[f:length: null]
21:38:19.654 [main] INFO o.s.bootstrap.sample.XmlSimpleUse - ele:[t:abc: null]
大家可以看上面的demo代碼,沒有依賴任何spring的類,基本還原了spring解析xml時的大體過程,在spring中多出來的細節部分,主要有兩處:
自定義entityResolver
docBuilder.setEntityResolver,這個部分,我們上面是默認實現。
大家看我們前面的xml,有一定了解的同學可能知道,前面定義了兩個namespace,語法一般是下面這樣的:
xmlns:namespace-prefix="namespaceURI"
所以,我們這邊的兩個namespace,前綴分別是f、t,內容分別是:
http://www.w3school.com.cn/furniture、http://www.w3school.com.cn/t
但是,我們一般xml文件是有格式要求的,比如spring里,比如
那,這個約束是在哪里呢?在namespaceURI
對應的dtd/xsd等文件中。
像上面截圖這樣,就是:
這一句,定義一個命名空間
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
// 下面這個,你要當成key/value來理解,key就是:http://www.springframework.org/schema/context,
//value,就是對應的xsd文件
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
有了上面的基礎知識,再來說那個接口:
public interface EntityResolver {
// 一般傳入的systemId即為后邊這樣的:http://www.springframework.org/schema/context/spring-context.xsd
public abstract InputSource resolveEntity (String publicId,
String systemId)
throws SAXException, IOException;
}
這個接口呢,就是讓我們自定義一個方法,來解析外部xml實體,一般傳入的參數如下:
即,publicId為null,systemId為xsd的uri,這個uri一般是可以通過網絡獲取的,比如:
http://www.springframework.org/schema/context/spring-context.xsd
但是,spring是自定義了自己的entityResolver
,實現類為:org.springframework.beans.factory.xml.ResourceEntityResolver
。
這個類,會在本地尋找對應的xsd文件,主要邏輯就是去查找classpath下的META-INF/spring.schemas
,我們可以看看spring-beans包內的該文件:
spring為什么要自定義EntityResolver呢,spring為啥要在本地找呢,原因是:
如果不自定義,jdk的dom解析類,就會直接使用http://www.springframework.org/schema/context/spring-context.xsd
這個東西,去作為URL,建立socket網絡連接來獲取。而部分環境,比如生產環境,基本是外網隔離的,你這時候是沒辦法去下載這個xsd文件的,豈不是就沒法校驗xml文件的語法、格式了嗎?
所以,spring要將這個外部的xsd引用,轉為在classpath下的查找。
自定義元素解析邏輯
這部分,大家再看下之前的骨架代碼:
Document document = docBuilder.parse(inputSource);
Element root = document.getDocumentElement();
log.info("root is {}",root);
NodeList nodeList = root.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
//遍歷每個元素,我們這里只是簡單輸出
if (node instanceof Element) {
Element ele = (Element) node;
log.info("ele:{}",ele);
}
}
骨架代碼里,遍歷每個元素,在spring里,遍歷到每個ele時,要去判斷對應的namespace,如果是默認的,交給xxx處理;如果不是默認的,要根據namespace找到對應的namespacehandler,具體大家可以看看上一節:
曹工說Spring Boot源碼(6)-- Spring怎么從xml文件里解析bean的
我們在前面說了,本講只先挑一個元素來講解,即util:constant 。
util-constant元素詳解
用法
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<util:constant id="chin.age" static-field=
"java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
</beans>
以上,即定義了一個常量bean,這個bean的值,就是static-field
中指定的,這里是java.sql.Connection#TRANSACTION_SERIALIZABLE
,值為8。
/**
* A constant indicating that
* dirty reads, non-repeatable reads and phantom reads are prevented.
* This level includes the prohibitions in
* <code>TRANSACTION_REPEATABLE_READ</code> and further prohibits the
* situation where one transaction reads all rows that satisfy
* a <code>WHERE</code> condition, a second transaction inserts a row that
* satisfies that <code>WHERE</code> condition, and the first transaction
* rereads for the same condition, retrieving the additional
* "phantom" row in the second read.
*/
int TRANSACTION_SERIALIZABLE = 8;
我們的測試代碼如下:
package org.springframework.utilnamespace;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.MyFastJson;
import java.util.List;
import java.util.Map;
@Slf4j
public class TestConstant {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath:util-namespace-test-constant.xml"},false);
context.refresh();
Map<String, Object> map = context.getDefaultListableBeanFactory().getAllSingletonObjectMap();
log.info("singletons:{}", JSONObject.toJSONString(map));
List<BeanDefinition> list =
context.getBeanFactory().getBeanDefinitionList();
MyFastJson.printJsonStringForBeanDefinitionList(list);
// Object bean = context.getBean("chin.age");
// System.out.println("bean:" + bean);
}
}
注意,這里,我們沒有調用getBean
等方法,我們只是簡單地,采用json輸出了其bean definition
,輸出如下:
{
"abstract":false,
"autowireCandidate":true,
"autowireMode":0,
// bean的class,好像是個factory,不是個int啊,我們那個bean,按理說,是int類型的
"beanClass":"org.springframework.beans.factory.config.FieldRetrievingFactoryBean",
"beanClassName":"org.springframework.beans.factory.config.FieldRetrievingFactoryBean",
"constructorArgumentValues":{
"argumentCount":0,
"empty":true,
"genericArgumentValues":[],
"indexedArgumentValues":{}
},
"dependencyCheck":0,
"enforceDestroyMethod":true,
"enforceInitMethod":true,
"lazyInit":false,
"lenientConstructorResolution":true,
"methodOverrides":{
"empty":true,
"overrides":[]
},
"nonPublicAccessAllowed":true,
"primary":false,
"propertyValues":{
"converted":false,
"empty":false,
// 這個是我們給這個常量bean設置的值,被放在了property
"propertyValueList":[
{
"converted":false,
"name":"staticField",
"optional":false,
"value":"java.sql.Connection.TRANSACTION_SERIALIZABLE"
}
]
},
"prototype":false,
"qualifiers":[],
"resolvedAutowireMode":0,
"role":0,
"scope":"",
"singleton":true,
"synthetic":false
}
我們放開前面注釋的兩行代碼:
Object bean = context.getBean("chin.age");
System.out.println("bean:" + bean);
output:
bean:8
有些同學估計有點蒙了,不要慌,這里簡單說下結論,因為util:constant被解析為了一個工廠bean,這個在上面json里也看到了,類型為:org.springframework.beans.factory.config.FieldRetrievingFactoryBean
。
當我們去getBean的時候,spring發現其為factory bean,就會調用這個工廠bean的工廠方法,去生產。
所以,這里呢,bean是誰?是那個工廠org.springframework.beans.factory.config.FieldRetrievingFactoryBean
,而不是這個工廠的產品:數字8。
具體的解析過程
根據namespaceUri獲得對應的namespaceHandler
從之前的骨架入手,spring里也有類似的代碼:
DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
// 這個方法,里面可以看到通過root得到了children,然后對children遍歷
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 {
// 這里,如果是<util:constant>,因為不是默認命名空間,所以走這里。
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
進入BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element)
:
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
//這里,通過namespaceUri,查找對應的handler,我們這里,會得到:org.springframework.beans.factory.xml.UtilNamespaceHandler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// 調用org.springframework.beans.factory.xml.UtilNamespaceHandler,解析constant元素
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
NamespaceHandler概覽
好了,我們看看這個UtilNamespaceHandler
:
public class UtilNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("constant", new ConstantBeanDefinitionParser());
registerBeanDefinitionParser("property-path", new PropertyPathBeanDefinitionParser());
registerBeanDefinitionParser("list", new ListBeanDefinitionParser());
registerBeanDefinitionParser("set", new SetBeanDefinitionParser());
registerBeanDefinitionParser("map", new MapBeanDefinitionParser());
registerBeanDefinitionParser("properties", new PropertiesBeanDefinitionParser());
}
private static class ConstantBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser {
@Override
protected Class getBeanClass(Element element) {
return FieldRetrievingFactoryBean.class;
}
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) {
String id = super.resolveId(element, definition, parserContext);
if (!StringUtils.hasText(id)) {
id = element.getAttribute("static-field");
}
return id;
}
}
...省略
}
這里,很明顯,UtilNamespaceHandler
定義了每個元素,該由什么類來處理。
namespaceHandler如何找到指定元素的parser
我們還是接着前面的代碼handler.parse(Element element, ParserContext parserContext)
往下看,是不是這樣吧:
父類 NamespaceHandlerSupport#parse
public BeanDefinition parse(Element element, ParserContext parserContext) {
//這里,繼續調用了本類的另一個方法來獲取Parser
return findParserForElement(element, parserContext).parse(element, parserContext);
}
// 這里就是上面調用的方法,來獲取Parser
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
// 這里的parser是一個map,map的元素就是來自於子類的init方法
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
這兩個方法是在父類中實現的,整體來說,NamespaceHandlerSupport
中維護了一個map,里面保存本namespace下,具體的元素及其對應的parser。
/**
* Stores the {@link BeanDefinitionParser} implementations keyed by the
* local name of the {@link Element Elements} they handle.
*/
private final Map<String, BeanDefinitionParser> parsers =
new HashMap<String, BeanDefinitionParser>();
我們看看,這個parser是什么時候存了東西進去的吧?通過find usage,發現如下方法會進行put操作:
NamespaceHandlerSupport#registerBeanDefinitionParser
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
這個方法還比較熟悉,因為在前面出現過了:
public class UtilNamespaceHandler extends NamespaceHandlerSupport {
private static final String SCOPE_ATTRIBUTE = "scope";
// 這個init方法,就是在之前通過namespaceUri來獲取對應的handler時,初始化的
@override
public void init() {
registerBeanDefinitionParser("constant", new ConstantBeanDefinitionParser());
registerBeanDefinitionParser("property-path", new PropertyPathBeanDefinitionParser());
registerBeanDefinitionParser("list", new ListBeanDefinitionParser());
registerBeanDefinitionParser("set", new SetBeanDefinitionParser());
registerBeanDefinitionParser("map", new MapBeanDefinitionParser());
registerBeanDefinitionParser("properties", new PropertiesBeanDefinitionParser());
}
parser實現類概述
通過上一節的講解,我們知道了UtilNamespaceHandler下的元素,及其對應的Parser。我們看看其類圖(圖小,可在單獨tab查看):
我們先通過其實現的接口,來了解其核心功能:
package org.springframework.beans.factory.xml;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
/**
* 該接口主要被DefaultBeanDefinitionDocumentReader使用,來處理頂級的,非默認命名空間下的的頂級元素(直接在<beans></beans>下)
* Interface used by the {@link DefaultBeanDefinitionDocumentReader} to handle custom,
* top-level (directly under {@code <beans/>}) tags.
*
* 實現類可以自由地通過該元素中的元數據,來轉換為任意多個BeanDefinition。(比如<context:component-scan></>)
* <p>Implementations are free to turn the metadata in the custom tag into as many
* {@link BeanDefinition BeanDefinitions} as required.
*
* Dom解析器,通過元素所在的命名空間,找到對應的NamespaceHandler,再從NamespaceHandler中找到對應的BeanDefinitionParser
* <p>The parser locates a {@link BeanDefinitionParser} from the associated
* {@link NamespaceHandler} for the namespace in which the custom tag resides.
*
* @author Rob Harrop
* @since 2.0
* @see NamespaceHandler
* @see AbstractBeanDefinitionParser
*/
public interface BeanDefinitionParser {
/**
* 解析指定的element,注冊其返回的BeanDefinition到BeanDefinitionRegistry
* (使用參數ParserContext#getRegistry()得到BeanDefinitionRegistry)
*/
BeanDefinition parse(Element element, ParserContext parserContext);
}
大家發現接口的意義了嗎,雖然前面的類,很復雜,但我們通過接口,可以馬上知道其核心功能。在這里,就是解析元素,獲得BeanDefinition
。
其實,到這里,基本可以回答,標題所提出的問題了,Spring解析xml,得到了什么?
從這個接口,可以知道,得到了BeanDefinition
!
UtilNamespaceHandler.ConstantBeanDefinitionParser
我們具體看看util:constant解析過程,這個類沒有直接實現parse方法,是在其父類實現的:
AbstractBeanDefinitionParser#parse
public final BeanDefinition parse(Element element, ParserContext parserContext) {
// 這個方法就是個骨架,用了模板方法設計模式,
// 1:調用另一個方法,獲取BeanDefinition
AbstractBeanDefinition definition = parseInternal(element, parserContext);
if (definition != null && !parserContext.isNested()) {
// 2:獲得id
String id = resolveId(element, definition, parserContext);
// 3:獲得別名
String name = element.getAttribute(NAME_ATTRIBUTE);
String[] aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
// 4:將beanDefinition的id、別名、definition等放進一個holder類
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
// 5:通過parserContext,得到BeanDefinitionRegistry,注冊本bean進去
registerBeanDefinition(holder, parserContext.getRegistry());
if (shouldFireEvents()) {
BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);
}
}
return definition;
}
我們上面,第一步的注釋那里,說用了模板設計模式,因為這個parseInternal是個抽象方法:
protected abstract AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext);
具體的實現,還在子類,鑒於這個類的層次有點深,我們再看看類圖:
這個parseInternal
就在``AbstractSingleBeanDefinitionParser`:
// 這個方法也足夠簡單,就是構造一個BeanDefinition,用了builder設計模式。
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
String parentName = getParentName(element);
// ConstantBeanDefinitionParser覆蓋了這個方法
Class<?> beanClass = getBeanClass(element);
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
}
else {
String beanClassName = getBeanClassName(element);
if (beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
if (parserContext.isDefaultLazyInit()) {
// Default-lazy-init applies to custom bean definitions as well.
builder.setLazyInit(true);
}
// 子類.AbstractSimpleBeanDefinitionParser重寫了這個方法
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
這里的getBeanClass
,在ConstantBeanDefinitionParser被重寫了,返回了一個工廠類class:
private static class ConstantBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser {
@Override
protected Class getBeanClass(Element element) {
return FieldRetrievingFactoryBean.class;
}
...
}
而doParse
,也在ConstantBeanDefinitionParser
的父類中AbstractSimpleBeanDefinitionParser
進行了重寫:
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
NamedNodeMap attributes = element.getAttributes();
// 這里,獲取element下的屬性
for (int x = 0; x < attributes.getLength(); x++) {
Attr attribute = (Attr) attributes.item(x);
if (isEligibleAttribute(attribute, parserContext)) {
String propertyName = extractPropertyName(attribute.getLocalName());
// 通過attribute.getValue()獲取屬性值,propertyName為屬性名,加入到BeanDefinition
builder.addPropertyValue(propertyName, attribute.getValue());
}
}
postProcess(builder, element);
}
回頭看看我們的xml:
<util:constant id="chin.age" static-field=
"java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
再看看我們debug時,此時的beanDefinition:
這里,獲取了beanDefinition后,就是進入到本小節開始的地方,去進行beanDefinition的注冊了。
總的來說,util:constant的解析,得到了一個BeanDefinition,其class類型為:
public class FieldRetrievingFactoryBean
implements FactoryBean<Object>, BeanNameAware, BeanClassLoaderAware, InitializingBean
這就是一個工廠bean。工廠bean在后續怎么被使用的,留待下一篇。
總結
本篇源碼在:
https://gitee.com/ckl111/spring-boot-first-version-learn/tree/master/all-demo-in-spring-learning/spring-xml-demo/src/main/java/org/springframework/utilnamespace中的TestConstant.java
xml的解析demo在:
我發現,一個東西,自己看懂可能還行,相對容易點,但是要把這個東西寫出來,卻是一個大工程。。。看似簡單的元素解析,你要把它講清楚,還真的要點篇幅,哈哈,所以,這也是為什么本篇比較長的原因。
總的來說,再次回答標題,spring到底得到了什么,得到了beanDefinition,本篇里,只得到了一個beanDefinition,還是工廠類型的;后面,我們會看到其他多種多樣的元素解析方式。
ok,就到這里,如果大家覺得有幫助,記得點贊。