上一篇介绍了Spring 是如何加载xml文件的,这一篇介绍如何解析xml。直接开始吧
/**
* 解析根节点 在DefaultBeanDefinitionDocumentReader这个类中
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//这里拿到根节点 判断是否是默认命名空间的标签 即<beans> <bean> <import> <alias>
if (delegate.isDefaultNamespace(root)) {
/*
* 获取子节点列表
* 这里注意一下这个方法 getChildNodes(),这个方法 看名字意思是获取子节点
* 其实 这里会获取到比子节点多的多的节点,这个方法会把回车 tab 空格 等 认为是一个节点,所以下面有一个if 判断
* 只有真正的节点才能转换为Element
*
*/
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); //把根节点当作非默认标签解析
}
}
下面介绍解析默认标签的方法(Element ele, BeanDefinitionParserDelegate delegate)
/**
* 在DefaultBeanDefinitionDocumentReader这个类中 解析默认标签节点元素
* delegate这个对象 我上一篇有介绍 是真正解析xml的工作类
* 这个方法基本不用做过的介绍,就是根据标签分别去做解析,我们具体解释下面的四个方法
*/
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { //<import>
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {//<alias>
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {//<bean>
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {<beans>
// recurse
doRegisterBeanDefinitions(ele);
}
}
我们继续分析上面四个方法
/**
* 解析import 标签
*/
protected void importBeanDefinitionResource(Element ele) {
String location = ele.getAttribute(RESOURCE_ATTRIBUTE); //这里首先获取location属性
if (!StringUtils.hasText(location)) {//如果这个属性是空的,直接报错返回
getReaderContext().error("Resource location must not be empty", ele);
return;
}
// Resolve system properties: e.g. "${user.dir}"
//这里根据注释可以看出是在处理location属性中的${}字符 ,也可以从其源码中分析处理,这里由于不是重点就不分析源码了
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
//new 一个set
Set<Resource> actualResources = new LinkedHashSet<Resource>(4);
// Discover whether the location is an absolute or relative URI
// 这里判断location 属性是绝对路径还是相对路径
boolean absoluteLocation = false;
try {
absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
}
catch (URISyntaxException ex) {
// cannot convert to an URI, considering the location relative
// unless it is the well-known Spring prefix "classpath*:"
}
// Absolute or relative?
if (absoluteLocation) { //如果是绝对路径则按照绝对路径去寻找资源再次解析
try {
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
if (logger.isDebugEnabled()) {
logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
}
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error(
"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
}
}
else {//如果是相对路径则按照相路径的资源再次解析
// No URL -> considering resource location as relative to the current file.
try {
int importCount;
Resource relativeResource = getReaderContext().getResource().createRelative(location);
if (relativeResource.exists()) {
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
actualResources.add(relativeResource);
}
else {
String baseLocation = getReaderContext().getResource().getURL().toString();
importCount = getReaderContext().getReader().loadBeanDefinitions(
StringUtils.applyRelativePath(baseLocation, location), actualResources);
}
if (logger.isDebugEnabled()) {
logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
}
}
catch (IOException ex) {
getReaderContext().error("Failed to resolve current resource location", ele, ex);
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
ele, ex);
}
}
Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
/**
* 解析alias 标签
*/
protected void processAliasRegistration(Element ele) {
String name = ele.getAttribute(NAME_ATTRIBUTE);
String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
boolean valid = true;
if (!StringUtils.hasText(name)) {
getReaderContext().error("Name must not be empty", ele);
valid = false;
}
if (!StringUtils.hasText(alias)) {
getReaderContext().error("Alias must not be empty", ele);
valid = false;
}
if (valid) {
try {
//其实这个方法里 最终就是执行这个方法
getReaderContext().getRegistry().registerAlias(name, alias);
}
catch (Exception ex) {
getReaderContext().error("Failed to register alias '" + alias +
"' for bean with name '" + name + "'", ele, ex);
}
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
}
//这是上面那个方法的实现,注册alias 我个人翻译一下 注册别名对象,就是把这个类的信息放到aliasMap这个容器中,就没了,没了,算是注册完成了。
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
if (alias.equals(name)) {
this.aliasMap.remove(alias);
}
else {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
}
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
}
}
/**
* 解析bean 标签
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance. 注册bean 到 beanfactory
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));
}
}
/**
* 这是上面方法的实现,可以理解为获取一个类的解释(我是看方法名字自译的),我觉得这个方法需要解释一下
*/
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<String>();
if (StringUtils.hasLength(nameAttr)) {//这里是处理name属性,如果中间有逗号,分号就切分弄成数组放到list中
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {//如果上面的list不是空的则把beanName 默认为第一个名称
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
//调用同名方法(增加了beanName参数,处理一些属性,返回一个AbstractBeanDefinition 对象,这个对象是BeanDefinitionHolder 对象中的一个属性,这个对象才是真正的类的解析,可以自行查看它的属性,都是对类的描述)
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {//如果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.isDebugEnabled()) {
logger.debug("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;
}
/**
*同名方法,其实上个方法的前半部分只是解析了id 和name属性,这里算是后半部分解析了其他属性,我们下面一一描述
*/
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {//解析class属性
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {//解析parent属性
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
AbstractBeanDefinition bd = createBeanDefinition(className, parent);//调用构造方法构造AbstractBeanDefinition 对象
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);//处理一些其他属性,具体不解释了,自己看一下 都能看明白,可以在配置文件中做相应的配置
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));//解析description
parseMetaElements(ele, bd);//解析meta属性,定义初始数据
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());//解析lookup-method属性
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());//解析replaced-method属性,这几个都好不常用。。我没有用到过,也不太清楚具体怎么用,不过我查了一下,有好多例子可以参考,这个需要经验才能确认在什么情况下使用
parseConstructorArgElements(ele, bd);//解析构造方法中的默认参数
parsePropertyElements(ele, bd);//解析property属性,默认属性赋值
parseQualifierElements(ele, bd);//接续qualifier属性
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
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;
}
这里有一句代码BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
根据字面意思是通过一个工具类注册对象的定义。看代码
//参数是一个BeanDefinitionHolder类,和 BeanDefinitionRegistry类,第一个参数是对象的定义,就是bean 第二个参数是beanFactory
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
处理beans的方法 就不分析了,自己看吧(如果自己看一眼的话会发现,这里是有的哦)
然后是解析自定义标签的方法 BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd)
自定义标签 可以理解为 不是上面四个默认标签以外的所有标签
/*
* 这里还有一层封装
*/
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
/*
* 这里是解析自定义标签的实现
*
*/
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
//先获取元素的命名空间 [context:component-scan: null]
//这里用这个来做示例,因为我本地xml的第一个自定义标签是这个
//这里获取到的是http://www.springframework.org/schema/context
String namespaceUri = getNamespaceURI(ele);
//然后根据命名空间获取处理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
//处理该元素 这里就是每个处理器单独处理了,回头在捡着几个重要的介绍。
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
这里解释一下NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
这句代码(记住这里哦,一会我们还要回到这里),本来应该在上一篇介绍的,上一篇忘了,这里补充一下
this.readerContext
这个对象是在创建delegate的时候初始化进来的
创建的方法this.delegate = createDelegate(getReaderContext(), root, parent)
在DefaultBeanDefinitionDocumentReader
类中
protected BeanDefinitionParserDelegate createDelegate(
XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
delegate.initDefaults(root, parentDelegate);
return delegate;
}
/**
* 类DefaultBeanDefinitionDocumentReader documentReader
*/
protected final XmlReaderContext getReaderContext() {
return this.readerContext;
}
上面方法中readerContext对象是获取的documentReader对象的,又要往上面追 documentReader中的registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
这个方法,可以看到把参数的readerContext赋值给了当前对象的readerContext属性
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
继续往前面追代码,可以看到最终是来源于createReaderContext(resource)
这个方法,这里resource这个对象不要有任何疑惑,它就是我们的Spring xml文件资源
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
然后看createReaderContext(resource)
这个方法
/**
* 进来看 其实这里也没啥,就是new了一个对象返回了,其实我们要看的是`getNamespaceHandlerResolver()`这个方法
*
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
。。。哇,这里进来又是一层,最后返回类一个DefaultNamespaceHandlerResolver类的对象,看到这里readContext这个对象就完了,暂时不多解释,回到刚才获取命名空间的那里继续
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
/**
*上面那个方法的实现
*/
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
}
现在回到NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
这里来啦,应该还能跟上面接上吧
现在 我们就知道 this.readerContext.getNamespaceHandlerResolver()
这句代码是得到了个啥东西了吧
没错就是DefaultNamespaceHandlerResolver
这个类的实例对象
然后这个对象里的NamespaceHandler resolve(String namespaceUri)
这个方法获取到了命名空间处理器,这里还得岔开解释DefaultNamespaceHandlerResolver
这个类的构造方法,我直接把源码都贴出来吧,加上了少量的注释,不多解释了,有不懂的可以在下面评论或者私信我,我觉得应该都能看懂。
这里需要注意的一个点 toString 方法中有调用getHandlerMappings()方法 所以 在idea中加断点看的时候 会发现map中已经有值了, 是因为idea会另外启动一个线程去调用toString 方法,不加断点就没有(这个真的是。。。)
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.xml;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
/**
* Default implementation of the {@link NamespaceHandlerResolver} interface.
* Resolves namespace URIs to implementation classes based on the mappings
* contained in mapping file.
*
* <p>By default, this implementation looks for the mapping file at
* {@code META-INF/spring.handlers}, but this can be changed using the
* {@link #DefaultNamespaceHandlerResolver(ClassLoader, String)} constructor.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @since 2.0
* @see NamespaceHandler
* @see DefaultBeanDefinitionDocumentReader
*/
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
//一个常量,默认配置文件的地址,这里有好多这个配置文件
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
//一个日志对象
protected final Log logger = LogFactory.getLog(getClass());
//一个类加载器
private final ClassLoader classLoader;
//一个字符串变量 其实是配文件路径
private final String handlerMappingsLocation;
//一个实时刷新的容器,用来存放处理器和命名空间的对象关系
private volatile Map<String, Object> handlerMappings;
//无参构造函数
public DefaultNamespaceHandlerResolver() {
this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
//带一个类加载器构造函数(也是我们上面使用的构造器)
public DefaultNamespaceHandlerResolver(ClassLoader classLoader) {
this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
//带一个类加载器,带一个配置文件地址的构造函数
public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {
Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
this.handlerMappingsLocation = handlerMappingsLocation;
}
//获取命名空间处理器
@Override
public NamespaceHandler resolve(String namespaceUri) {
Map<String, Object> handlerMappings = getHandlerMappings();
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
String className = (String) handlerOrClassName;
try {
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "] not found", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "]: problem with handler class file or dependent class", err);
}
}
}
//加载处理器和命名空间的映射
private Map<String, Object> getHandlerMappings() {
if (this.handlerMappings == null) {
synchronized (this) {
if (this.handlerMappings == null) {
try {
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return this.handlerMappings;
}
@Override
public String toString() {
return "NamespaceHandlerResolver using mappings " + getHandlerMappings();
}
}
这里是好多个spring.handlers中一个的内容
http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
具体的自定义标签就要看具体的handler处理了。我后面会写几个常用的handler的使用,这篇就先到这里吧。