Spring源码情操陶冶-DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
前言-阅读源码有利于陶冶情操,本文承接前文Spring源码情操陶冶-AbstractApplicationContext#obtainFreshBeanFactory
前文提到最关键的地方是解析bean xml配置文件,废话不多说,直接上代码清单
//正如官方注释所说,解析import标签、alias标签、bean标签和自定义的标签
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//检查<beans>根标签的命名空间是否为空或者是http://www.springframework.org/schema/beans
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))
//解析import标签、alias标签、bean标签、内置<beans>标签
parseDefaultElement(ele, delegate);
}
else {
//解析自定义标签,例如<context:component-scan basePackage="" />
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
那我们根据两个部分来解读对bean xml文件的解析,分别为DefaultBeanDefinitionDocumentReader#parseDefaultElement
和BeanDefinitionParserDelegate#parseCustomElement
DefaultBeanDefinitionDocumentReader#parseDefaultElement
解析import标签、alias标签、bean标签,代码清单如下
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//import标签解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//alias标签解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//bean标签解析
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse 递归
doRegisterBeanDefinitions(ele);
}
}
三种解析具体方法
1.DefaultBeanDefinitionDocumentReader#importBeanDefinitionResource
import
标签解析代码比较长,逻辑倒是很简单,代码清单如下
protected void importBeanDefinitionResource(Element ele) {
//获取<import>标签的resource属性值
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
//resource不允许为空
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
// Resolve system properties: e.g. "${user.dir}"
//这里强调下,对于<import>标签,其会从System.getProperties()和System.getenv()中属性获取,优先System.getProperties()
//即优先系统变量,环境变量次之
location = environment.resolveRequiredPlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<Resource>(4);
// Discover whether the location is an absolute or relative URI
//resource属性支持URL模式,找到相应的资源并进行加载
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));
}
2.DefaultBeanDefinitionDocumentReader#processAliasRegistration
主要目的就是缓存全局别名
protected void processAliasRegistration(Element ele) {
//这里确保<alias>标签的name、alias值不为空 e.g. <alias name="key" alias="value">
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 {
//全局设置,查阅后其会保存到DefaultListableFactory的父级类`SimpleAliasRegistry#aliasMap`中
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));
}
}
3.DefaultBeanDefinitionDocumentReader#processBeanDefinition
<bean>
标签的解析处理
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//初始化bean标签的相应内容
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//对bean标签中的属性和子标签,进行相应的具体解析,例如property、constructor-args
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
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#parseBeanDefinitionElement
解析bean
标签,也许会返回null在出现解析错误的时候,代码清单如下
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
//获取<bean>的 id属性
String id = ele.getAttribute(ID_ATTRIBUTE);
//如果有name属性,则获取name,并且可有多个name,以,;为分隔符
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<String>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
//当id属性不存在且name属性有值时,默认使用第一个name值
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
//确保beanName的唯一性,即在应用使用前不允许有两个的beanName一致
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
//解析除id、name属性的情况
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
//省略部分代码
...
...
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
限于篇幅太长,其余的情况我们就不讲解了,有兴趣的可以自行前往,但在此根据读到的源码对bean
解析进行总结
parent
属性代表父类,这里表示的是父类的唯一id名不允许有
singleton
属性;可有scope
属性;
可有abstract
属性,值可为true/false;
可有lazyinit
属性,值为default则沿用beans
标签中的lazyinit
,否则为true/false;
可有autowire
属性,值为default则沿用beans
标签的autowire
。byName
表示按照名称注入、byType
按照类型注入、constructor
表明按照构造函数注入、autodetect
表明自动检测(官方不建议使用)、默认则不使用注入;
可有dependency-check
属性,值有all
、objects
、simple
,默认为不检测;
可有depends-on
属性,可有多值,以,;为分隔符;
可有autowire-candidate
属性,值可为true/false/default/空字符串
;
可有primary
属性,值为true/false;
可有init-method
属性,初始化方法,对应可有destroy-method
属性;
可有facroty-method
属性,工厂方法;
可有factory-bean
属性
bean
标签下可有description
标签,表示对此bean
的描述;
也可有meta
标签;
可有lookup-method
标签;
可有replaced-method
标签;
可有constructor-arg
标签;
可有property
标签,其中value
和ref
属性只可选用一个;
可有qualifier
标签,其下可有attribute
标签,同@Qualifier
注解所有的bean信息都由
BeanDefinitionHolder
对象来保存,其中的BeanDefinition
包含了一个bean
标签的所有可能内容
BeanDefinitionParserDelegate#parseCustomElement
解析自定义的标签节点,例如<context:component-scan>
、<tx:annotation-driven>
等,简单看下代码清单
//containingBd此处为null
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
//获取命名空间
String namespaceUri = getNamespaceURI(ele);
//从map集合中获取NamespaceHandler接口
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));
}
按照上述的注释,我们需要查看下namespaceURI
对应的NamespaceHandler
到底是如何获取的,真实调用的是DefaultNamespaceHandlerResolver
类,稍微分析下此类
构造函数
public DefaultNamespaceHandlerResolver() {
//第二个参数值为 META-INF/spring.handlers
this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
//此处所用的是handlerMappingLocation,指的是handler接口类加载的文件资源
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;
}
resolve解析方法
public NamespaceHandler resolve(String namespaceUri) {
//调用的是PropertiesLoaderUtils.loadAllProperties读取所有classpath下的'META-INF/spring.handlers'文件,保存至Map集合
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);
//声明的handler必须是NamespaceHandler的实现类
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
//实例化Handler类
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);
}
}
}
限于篇幅太长,关于bean解析以及custom的具体解析未来会分条详细介绍,敬请期待
下节预告
Spring源码情操陶冶-AbstractApplicationContext#prepareBeanFactory