解析import标签
在注册BeanDefinition
中知道解析Bean有两种方式:
- 默认解析方式
parseDefaultElement()
- 自定义解析方式
parseCustomElement()
默认解析过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public static final String ALIAS_ATTRIBUTE = "alias"; public static final String IMPORT_ELEMENT = "import";
public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT; public static final String NESTED_BEANS_ELEMENT = "beans";
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { doRegisterBeanDefinitions(ele); } }
|
import示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<context:property-placeholder location="classpath:application.properties"/> <import resource="spring-${suffix1}.xml"/> <import resource="spring-${suffix2}.xml"/> </beans>
suffix1 = name1 suffix2 = name2
|
一个applicationContext.xml
配置文件中可以使用import
标签导入其他模块的配置文件。维护成本较低。
importBeanDefinitionResource()方法
DefaultBeanDefinitionDocumentReader.importBeanDefinitionResource()
,如方法注释:解析import标签,并将给定资源中的bean加载到bean工厂中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| protected void importBeanDefinitionResource(Element ele) { String location = ele.getAttribute(RESOURCE_ATTRIBUTE); if (!StringUtils.hasText(location)) { getReaderContext().error("Resource location must not be empty", ele); return; } location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location); Set<Resource> actualResources = new LinkedHashSet<>(4); boolean absoluteLocation = false; try { absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); } catch (URISyntaxException ex) { } if (absoluteLocation) { try { 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 { 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.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)); }
|
判断路径
判断location
是相对路径还是绝对路径:
1
| absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
|
ResourcePatternUtils.isUrl(location)
以 classpath*:
或者 classpath:
开头的或者通过resourceLocation
创建的java.net.URL
为绝对路径。
方法具体内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public static boolean isUrl(@Nullable String resourceLocation) { return (resourceLocation != null && ( resourceLocation.startsWith(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX) || ResourceUtils.isUrl(resourceLocation) ) ); }
public static boolean isUrl(@Nullable String resourceLocation) { if (resourceLocation == null) { return false; } if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) { return true; } try { new URL(resourceLocation); return true; } catch (MalformedURLException ex) { return false; } }
public static URI toURI(String location) throws URISyntaxException { return new URI(StringUtils.replace(location, " ", "%20")); }
public boolean isAbsolute() { return scheme != null; }
|
处理绝对路径
如果是绝对路径,执行int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
,该方法定义在AbstractBeanDefinitionReader
中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available"); }
if (resourceLoader instanceof ResourcePatternResolver) { try { Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location); int count = loadBeanDefinitions(resources); if (actualResources != null) { Collections.addAll(actualResources, resources); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]"); } return count; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { Resource resource = resourceLoader.getResource(location); int count = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location [" + location + "]"); } return count; } }
|
- 首先,获取 ResourceLoader 对象。
- 然后,根据不同的 ResourceLoader 执行不同的逻辑,主要是可能存在多个 Resource 。
- 最终,都会回归到
XmlBeanDefinitionReader#loadBeanDefinitions(Resource... resources)
方法,所以这是一个递归的过程。
- 另外,获得到的 Resource 的对象或数组,都会添加到
actualResources
中。
处理相对路径
如果 location
是相对路径,则会根据相应的 Resource 计算出相应的相对路径的 Resource 对象 ,然后:
- 若该 Resource 存在,则调用
XmlBeanDefinitionReader#loadBeanDefinitions()
方法,进行 BeanDefinition 加载。
- 否则,构造一个绝对
location
( 即 StringUtils.applyRelativePath(baseLocation, location)
处的代码),并调用 #loadBeanDefinitions(String location, Set actualResources)
方法,与绝对路径过程一样。
小结
解析import
的过程:获取 source 属性值,得到正确的资源路径,然后调用 XmlBeanDefinitionReader#loadBeanDefinitions(Resource... resources)
方法,进行递归的 BeanDefinition 加载。