17--Spring解析xml默认命名空间
上一节分析了Spring将xml文件转换为Document对象的过程,接下来Spring会对已转换的Document对象进行解析。打开XmlBeanDefinitionReader类的registerBeanDefinitions方法。打开DefaultBeanDefinitionDocumentReader类的doRegisterBeanDefinitions方法。
1. 引言
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 1、创建documentReader对象
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 2、读取已经注册的bean个数
int countBefore = getRegistry().getBeanDefinitionCount();
// 3、解析Document并注册BeanDefinition
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 4、返回本次注册的bean个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
该方法总共分为了四步,第三步是执行对Document对象解析和注册BeanDefinition。先来看对Document对象的解析过程。
2.doRegisterBeanDefinitions方法分析
protected void doRegisterBeanDefinitions(Element root) {
// 1、创建BeanDefinitionParserDelegate对象,用来解析Element元素
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
// 2、解析并验证profile节点,如果配置了profile属性,则验证当前环境是否激活了对应的profile节点,
// 用于多开发环境配置,该方式在开发中已不多见。
// 例如:System.setProperty("spring.profiles.active", "dev");
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);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}
}
// 3、解析前置处理,空的模板方法
preProcessXml(root);
// 4、解析并注册BeanDefinition
parseBeanDefinitions(root, this.delegate);
// 5、解析后置处理,空的模板方法
postProcessXml(root);
this.delegate = parent;
}
doRegisterBeanDefinitions方法并没有自己去执行对xml节点的解析而是委托给了BeanDefinitionParserDelegate,通过代理去解析xml节点。Spring的xml文件又可能包含默认命名空间和自定义命名空间,我们来看Spring是如何进行处理的。
3.parseBeanDefinitions方法简析
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 1、解析默认命名空间
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
// 默认命名空间
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
// 自定义命名空间
else {
delegate.parseCustomElement(ele);
}
}
}
}
// 2、解析自定义命名空间
else {
delegate.parseCustomElement(root);
}
}
这与上一节的xml解析分析大致相同,只是增加了对命名空间的判断,针对不同的命名空间使用不同的方法进行解析。先来看对默认命名空间中的标签的解析。
4.parseDefaultElement解析默认标签
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 1、解析import标签,并注册beanDefinition
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 2、解析alias标签,并注册别名
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// 3、解析bean标签,并注册beanDefinition
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// 4、解析beans标签
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
doRegisterBeanDefinitions(ele);
}
}
代码到这里,相信大家已经有所了然了,日常开发中的常见标签已经展现出来了(虽然现在提倡无配置文件,但是还是要对其原理有所了解)。parseDefaultElement方法对import、alias、bean、beans四种标签分别做了单独处理,bean标签的解析最复杂也是一个比较基础的操作,先来看对bean标签的解析,如果大家能够了解bean的解析,那么其他的解析过程相信大家都能看懂,因为相对而言要比解析bean标签简单的多。
5.解析Bean标签
/**
* 解析bean标签将其转换为definition并注册到BeanDefinitionRegistry
* Process the given bean element, parsing the bean definition and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 1、将解析的节点信息封装至BeanDefinitionHolder对象
// BeanDefinitionHolder-->封装了BeanDefinition,beanName以及aliases
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 2、装饰BeanDefinition
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 3、执行注册
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
// 4、发送注册事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
可以看到Spring将解析之后的内容封装至了BeanDefinitionHolder类,该类封装了BeanDefinition,beanName以及aliases,BeanDefinition封装了bean标签的各种信息。
该方法还执行了对BeanDefinition的注册,留在下节分析。来看bean标签解析的具体过程。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 1、解析bean的id和bean的别名
// 获取id标签属性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取name(别名)标签属性,缓存至List集合
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
// 如果bean的id为空,但是别名不为空的话,那么默认采用第一个别名作为beanName
// 例如:<bean class="com.lyc.cn.v2.day01.Dog" name="myDog1,myDog2"/>,使用myDog1作为beanName
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
}
// 2、containingBean不为空,则检查beanName和别名是否被使用
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 3、解析bean标签,将其转换为BeanDefinition对象
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
// 4、如果beanDefinition且未配置bean的id属性,name属性,则为当前bean生成id和别名
// 例如:<bean class="com.lyc.cn.v2.day01.Dog"/>,<bean factory-bean="dog4"/>,<bean parent="outer"/>等
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
// containingBean不为null,则当前bean是内部bean,使用BeanDefinitionReaderUtils生成beanName
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
} else {
// 否则,使用XmlReaderContext对象生成beanName
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.
// 如果生成器返回的是类名加上后缀,则为普通bean类名注册一个别名。这在Spring 1.2/2.0的向后兼容性中是可以实现的。
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null
&& beanName.startsWith(beanClassName)
&& beanName.length() > beanClassName.length()
&& !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
} catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
// 5、创建并返回BeanDefinitionHolder对象
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
首先对id,name属性进行解析,该过程比较简单,接下来就是对bean中其他属性的解析。来看parseBeanDefinitionElement方法。
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
// 1、解析class属性
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
// 2、解析parent属性
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 3、创建AbstractBeanDefinition对象
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 4、解析bean标签属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 5、解析meta标签
parseMetaElements(ele, bd);
// 6、解析lookup-method属性
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 7、解析replace-method属性
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 8、解析构造函数参数
parseConstructorArgElements(ele, bd);
// 9、解析property属性
parsePropertyElements(ele, bd);
// 10、解析qualifier属性
parseQualifierElements(ele, bd);
// 11、设置bean定义来源和元数据的来源
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;
}
该方法涉及的细节较多,篇幅限制,只对其中比较重要比较常见的方法进行分析。分析第四步解析bean标签属性。
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean,
AbstractBeanDefinition bd) {
// 1、设置bean作用域
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.
// 未明确指定bean的作用域,且当前被解析bean是内部bean的话,
// 则默认使用outer bean的的作用域作为当前bean的作用域
// 例如:下面的配置,解析到inner属性时,inner未指定作用域,则使用outer的作用域,也就是prototype
/**
<bean id="outer" class="com.lyc.cn.v2.day01.inner.Outer" scope="prototype">
<property name="inner">
<bean id="inner" class="com.lyc.cn.v2.day01.inner.Inner"/>
</property>
</bean>
**/
bd.setScope(containingBean.getScope());
}
// 2、设置abstract属性
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
// 3、设置lazy-init(延迟加载)属性;
// 如果该属性为true的话,ApplicationContext容器在初始化时不会加载该bean;
// 而是在第一次向容器索取该bean时才会被初始化
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (DEFAULT_VALUE.equals(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
// 4、设置autowire属性,此属性默认不开启
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
// 5、设置depends-on属性,如果BeanA依赖于BeanB,可通过depends-on属性使BeanB在BeanA之前完初始化
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
// 6、设置autowire-candidate属性,当一个接口有多个实现类时,
// 配置autowire-candidate属性可以明确指定实现类是否参与自动注入
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(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));
}
// 7、设置primary属性,当byType注入有多个类型时,
// 可以指定primary="true",提高注入的优先级,避免抛出异常
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
// 8、设置init-method,bean初始化完成后回调该方法
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
bd.setInitMethodName(initMethodName);
}
else if (this.defaults.getInitMethod() != null) {
// 尝试从DocumentDefaultsDefinition对象中获取init-method属性
// DocumentDefaultsDefinition对象保存了Spring bean的一些简单设置,
// 我们可以通过该类设定通用的bean属性模板
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
// 9、设置destroy-method属性,bean销毁后回调该方法
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
}
else if (this.defaults.getDestroyMethod() != null) {
// 尝试从DocumentDefaultsDefinition对象中获取destroy-method属性
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
// 10、设置factory-method属性,该属性可指定静态工厂或实例工厂方法实例化bean
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
// 11、设置factory-bean属性,一般和factory-method属性一起使用,
// 指定工厂bean和工厂bean的工厂方法
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}
其他的方法大家可以通过在配置文件中配置属性,debug跟踪调试,不在一一赘述。到这里Spring已经将xml中的配置信息封装至BeanDefinitionHolder对象,接下来就可以执行对bean的注册了。
另外对bean name的生成策略简单分析一下:
上面parseBeanDefinitionElement方法已经有所简介,来看一下具体过程:
public static String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean) throws
BeanDefinitionStoreException {
// 1、获取bean的className
String generatedBeanName = definition.getBeanClassName();
// 2、如果generatedBeanName为null,
// 则判断其是否有parent属性或者factory-bean并生成generatedBeanName
if (generatedBeanName == null) {
// parent属性不为空
if (definition.getParentName() != null) {
generatedBeanName = definition.getParentName() + "$child";
}
// factory-bean属性不为空
else if (definition.getFactoryBeanName() != null) {
generatedBeanName = definition.getFactoryBeanName() + "$created";
}
}
// 3、如果上述方法均无法生成generatedBeanName,则抛出异常
if (!StringUtils.hasText(generatedBeanName)) {
throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither "
+ "'class' nor 'parent' nor 'factory-bean' - can't "
+ "generate bean name");
}
// 4、判断是否内部bean,并根据生成的generatedBeanName,拼接最终的beanName返回
String id = generatedBeanName;
if (isInnerBean) {
// Inner bean: generate identity hashcode suffix.
// GENERATED_BEAN_NAME_SEPARATOR-->#
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
} else {
// Top-level bean: use plain class name.
// Increase counter until the id is unique.
int counter = -1;
while (counter == -1 || registry.containsBeanDefinition(id)) {
counter++;
// GENERATED_BEAN_NAME_SEPARATOR-->#
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
}
}
return id;
}
这一部分的代码比较多,也比较细,一时间找不到应该如何写才能让读者更清晰的了解解析过程,感兴趣的多多调试跟踪代码吧。