spring源码日记07: import标签解析
2020-02-11 本文已影响0人
BugPool
所有文章已迁移至csdn,csdn个人主页https://blog.csdn.net/chaitoudaren
本节介绍一下import标签的解析,import标签用于引进新的资源文件,例如一个spring.xml需要引入redis,jdbc等配置文件。主要是要像读者介绍一下递归加载的思想,包括beans标签也使用到了递归的思想,不过beans是递归加载bean,而import是递归加载资源文件。
<beans>
<import resource="redis.xml">
<import resource="${jdbc.url}">
<beans>
// DefaultBeanDefinitionDocumentReader.java
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);
}
// 解析beans标签,解析该标签实际是递归调用doRegisterBeanDefinitions,前面提到的parent delegate
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
解析import标签
- location 有可能等于${jdbc.url},他将被系统变量替换成对应的URL
- 代码的核心还是loadBeanDefinitions方法的递归调用,该方法我们在spring源码日记03: 资源文件读取
就曾介绍过。细心的人可能会发现参数不同,这都是重载,调用核心还是我们讲过的方法
// DefaultBeanDefinitionDocumentReader.java
protected void importBeanDefinitionResource(Element ele) {
// 获取标签下resource属性
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
// Resolve system properties: e.g. "${user.dir}"
// 转化系统变量 e.g. "${jdbc.url}" 需要转化为对应地址
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
// 每一个被实际加载的资源都会被加入这个set中
// 在资源被成功加载后,通知import-processed事件哪些资源文件又被加载了
Set<Resource> actualResources = new LinkedHashSet<>(4);
// Discover whether the location is an absolute or relative URI
// 判断是绝对路径还是相对路径
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 {
// 绝对路径,直接使用递归调用
// 这里的loadBeanDefinitions肯定多少有点印象
// 该函数是loadBeanDefinitions(EncodedResource encodedResource)的重载,将会递归的加载资源
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
if (logger.isTraceEnabled()) {
logger.trace("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;
// 相对路径,则需要先验证路径下是否有文件,之后直接调用递归的loadBeanDefinitions加载文件
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.isTraceEnabled()) {
logger.trace("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[0]);
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}