您现在的位置是:首页 >其他 >【源码分析】Spring如何解析xml文件生成BeanDefinition网站首页其他
【源码分析】Spring如何解析xml文件生成BeanDefinition
4.1 Spring如何解析xml文件生成BeanDefinition
源码版本:5.3.x | 构建一套本地Spring源码,学习起来真的是太方便了!
从容器启动开始不断Debug,发现Spring中将xml文件的bean的信息转换为
BeanDefinition
的类为XmlBeanDefinitionReader
等一些列的类
下面将直接从核心方法开始进行一步步解析。
XmlBeanDefinitionReader
类—>loadBeanDefinitions
方法:
将需要进行加载的xml文件转换为流对象信息等,并调用本类的doLoadBeanDefinitions
方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
// 为空校验
Assert.notNull(encodedResource, "EncodedResource must not be null");
// 判断日志是否开启
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
// 存储当前要被解析的xml文件资源信息
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
// 判断是否循环加载了该文件
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
/**
* 获取当前xml文件的流信息,转换为BufferedInputStream流等信息,并进行加载Bean定义信息
* inputSource: 当前xml文件的流信息
* encodedResource.getResource():当前xml文件的地址信息,以及一些类加载器的信息
*/
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
XmlBeanDefinitionReader
类—>doLoadBeanDefinitions
方法:
读取xml文件 转换为 org.w3c.dom.Document
对象,继续调用XmlBeanDefinitionReader
类的registerBeanDefinitions
方法,解析Document对象,注册BeanDefinition
这个方法本身代码比较简单,下面是我把附属的catch捕获的异常删除后的代码:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
// 读取xml文件 转换为 org.w3c.dom.Document 对象
Document doc = doLoadDocument(inputSource, resource);
// 根据Document对象 解析bean信息。创建beanDefinition信息
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
XmlBeanDefinitionReader
类的registerBeanDefinitions
方法:
这段代码比较简单,就是计算一下前后加载了多少beanDifinition
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// 这里开始解析Document对象,并添加BeanDefinition信息
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
到这里XmlBeanDefinitionReader
的职责算是完成了,剩下的便是解析转换后的Document对象
继续跟进这个方法:documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
来到了DefaultBeanDefinitionDocumentReader
类中
获取XmlReaderContext
对象,解析Document中的节点信息
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
// 获取XmlReaderContext 对象
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
继续跟进 doRegisterBeanDefinitions(doc.getDocumentElement());
方法:
核心代码:
protected void doRegisterBeanDefinitions(Element root) {
......
// 这个里面是个空方法,估计是一个标志。。
preProcessXml(root);
// 将Document 转换为BeanDefinition
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
parseBeanDefinitions(root, this.delegate)
:真正开始解析xml文件
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 判断当前的xml根标签的命名空间是否是自定义的
if (delegate.isDefaultNamespace(root)) {
// 获取<beans></beans>标签中所有的子标签
NodeList nl = root.getChildNodes();
// 对子标签进行遍历解析
for (int i = 0; i < nl.getLength(); i++) {
// 获取子标签每一个节点
Node node = nl.item(i);
if (node instanceof Element) {
/**
* Element extends Node ,将节点转换为元素,容易获取内容
*/
Element ele = (Element) node;
// 判断当前元素的xml命名空间是否是自定义的
if (delegate.isDefaultNamespace(ele)) {
// 解析当前节点 TODO: 这里开始解析每个标签的元素
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
parseDefaultElement(ele, delegate);
:
进行判断要处理的那些标签
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 判断当前标签是不是import `IMPORT_ELEMENT` = import
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
// 处理import的标签
importBeanDefinitionResource(ele);
}
// 判断当前标签是不是alias `ALIAS_ELEMENT` = alias
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
// 处理alias的标签
processAliasRegistration(ele);
}
// 判断当前标签是不是bean `BEAN_ELEMENT` = bean
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
// 处理bean标签的信息
processBeanDefinition(ele, delegate);
}
// 这个是比较特殊的 处理 beans标签中的 beans标签
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
这里因为是看bean标签是如何变成BeanDefinition的,所以只看processBeanDefinition(ele, delegate)
这个方法
processBeanDefinition(ele, delegate)
:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 这里便获取到了bean的定义信息
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 获取当前标签上的一些属性,然后添加到BeanDefinition里
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 将当前bean的定义的信息 注册到BeanDefinitionMap中去
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));
}
}
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele)
这里获取bean的名字,后面还涉及到了生成bean的名字之类的
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 获取id
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取name
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 因为name可以填写多个,所以把name存放在集合里面
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
// 如果没设置id 则会把第一个别名(name)当做beanName
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);
}
// 根据beanName 重新生成bean定义信息
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
// 如果还是没名字的话,就生成一个
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
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;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
总结:
Spring将Xml文件转换为Document对象,再对Document对象中的元素进行遍历、解析、分类、存储等。