The location can also be a location pattern, provided that the * ResourceLoader of this bean definition reader is a ResourcePatternResolver. * @param location the resource location, to be loaded with the ResourceLoader * (or ResourcePatternResolver) of this bean definition reader * @param actualResources a Set to be filled with the actual Resource objects * that have been resolved during the loading process. May be {@code null} * to indicate that the caller is not interested in those Resource objects. * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors * @see #getResourceLoader() * @see #loadBeanDefinitions(org.springframework.core.io.Resource) * @see #loadBeanDefinitions(org.springframework.core.io.Resource[]) */ public int loadBeanDefinitions(String location, @Nullable Set actualResources) throws BeanDefinitionStoreException { // 获得 ResourceLoader 对象 ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { // 获得 Resource 数组,因为 Pattern 模式匹配下,可能有多个 Resource 。例如说,Ant 风格的 location Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); // 加载 BeanDefinition 们 int count = loadBeanDefinitions(resources); // 添加到 actualResources 中 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 { // Can only load single resources by absolute URL. // 获得 Resource 对象, Resource resource = resourceLoader.getResource(location); // 加载 BeanDefinition 们 int count = loadBeanDefinitions(resource); // 添加到 actualResources 中 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` 中。 ### 2.3 处理相对路径 如果 `location` 是相对路径,则会根据相应的 Resource 计算出相应的相对路径的 Resource 对象 ,然后: - 若该 Resource 存在,则调用 `XmlBeanDefinitionReader#loadBeanDefinitions()` 方法,进行 BeanDefinition 加载。 - 否则,构造一个绝对 `location`( 即 `StringUtils.applyRelativePath(baseLocation, location)` 处的代码),并调用 `#loadBeanDefinitions(String location, Set actualResources)` 方法,**与绝对路径过程一样**。 ## 3. 小结 至此,`import` 标签解析完毕,整个过程比较清晰明了:**获取 source 属性值,得到正确的资源路径,然后调用 XmlBeanDefinitionReader#loadBeanDefinitions(Resource... resources) 方法,进行递归的 BeanDefinition 加载**。