Spring 源码解析之 spring-core

477 阅读2分钟

摘要:

Spring 源码 core 模块关于配置文件的解析与 Bean 的加载研究

spring-core

基本

配置文件: spring-PeppaPig.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="user" class="com.peggy.dao.User"/>
</beans>

启动代码: Test.java

public class Test {
    public static void main(String[] args) {
        ApplicationContext context=new ClassPathXmlApplicationContext("spring-${username}.xml");
    }
}

注入对象: User.java

public class User {
    private String id;
    private String userName;
}

ClassPathXmlApplicationContext

继承关系 流程图 如下:

ClassPathXmlApplicationContext

启动类:

进入到启动类中,构造方法会一直调用父类构造直到 AbstractApplicationContext

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
    this(new String[] {configLocation}, true, null);
}
​
public ClassPathXmlApplicationContext(
    String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
    throws BeansException {
    //调用父类构造方法,进行相关对象的创建等操作
    super(parent);
    //设置赔配置资源的路径
    setConfigLocations(configLocations);
    if (refresh) {
        //核心步骤与核心流程
        refresh();
    }
}
public AbstractXmlApplicationContext(@Nullable ApplicationContext parent) {
    super(parent);
}
public AbstractRefreshableConfigApplicationContext(@Nullable ApplicationContext parent) {
    super(parent);
}
public AbstractRefreshableApplicationContext(@Nullable ApplicationContext parent) {
    super(parent);
}
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
    this();
    setParent(parent);
}
public AbstractApplicationContext() {
    //创建资源模式处理器(用于解析当前系统运行的时候所需要的一系列资源- 写的配置文件 写的 xml 文件)
    this.resourcePatternResolver = getResourcePatternResolver();
}

protected ResourcePatternResolver getResourcePatternResolver() {
    //创建一个资源模式解析器(其实就是用来解析 xml 配置文件)
    return new PathMatchingResourcePatternResolver(this);
}

这里最终调用 getResourcePatternResolver() 方法 ,创建一个用于解析 xml 的资源解析器。

设置配置文件路径

image-20230625185946846

public void setConfigLocations(@Nullable String... locations) {
	if (locations != null) {
		Assert.noNullElements(locations, "Config locations must not be null");
		this.configLocations = new String[locations.length];
		for (int i = 0; i < locations.length; i++) {
			//解析给定路径
			this.configLocations[i] = resolvePath(locations[i]).trim();
		}
	}
	else {
		this.configLocations = null;
	}
}

解析给定路径: resolvePath

protected String resolvePath(String path) {
    return getEnvironment().resolveRequiredPlaceholders(path);
}

此方法的作用是将 path 中的变量进行解析,比如将 new ClassPathXmlApplicationContext("spring-${username}.xml"); 中的 ${username} 解析成主机名。例如解析为 spring-peggy.xml

image-20230625190945420

getEnvironment 的方法内部是很简单的,对当前对象的环境进行一个判断如果为空,则通过 createEnvironment() 进行创建一个StandardEnvironment 对象。

protected ConfigurableEnvironment createEnvironment() {
	return new StandardEnvironment();
}

Environment 接口体系

image-20230625193434334

路径Placeholder处理

private final ConfigurablePropertyResolver propertyResolver =
			new PropertySourcesPropertyResolver(this.propertySources);
@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
    // text 文件的配置路径 spring-${username}.xml
	return this.propertyResolver.resolveRequiredPlaceholders(text);
}
@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
	if (this.strictHelper == null) {
		this.strictHelper = createPlaceholderHelper(false);
	}
	return doResolvePlaceholders(text, this.strictHelper);
}
PropertyResolver接口继承关系

image-20230626211253465

解析

AbstractPropertyResolver.reresolveRequiredPlaceholders

@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
	if (this.strictHelper == null) {
		this.strictHelper = createPlaceholderHelper(false);
	}
	return doResolvePlaceholders(text, this.strictHelper);
}
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
	return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
			this.valueSeparator, ignoreUnresolvablePlaceholders);
}

image-20230626214606741

doResolvePlaceholders

private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
	return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}

在 spring 源码中,往往以 do * 开头的方法都是具体执行逻辑业务的主要方法,这里的 doReslovePlaceholders 主要完成的就是解析之前在 ClassPathXmlApplicationContext("spring-${username}.xml");中的占位符解析。这里主要由 PropertyPlaceholderHelper.replacePlaceholders 完成。

public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
   Assert.notNull(value, "'value' must not be null");
   return parseStringValue(value, placeholderResolver, null);
}

image-20230626220331409

可以看到的是,真正的解析还没有开始,主要的解析过程在 PropertyPlaceholderHelper.parseStringValue 中执行。

parseStringValue 占位符解析

protected String parseStringValue(
			String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {

		int startIndex = value.indexOf(this.placeholderPrefix);
		if (startIndex == -1) {
			return value;
		}

		StringBuilder result = new StringBuilder(value);
		while (startIndex != -1) {
			int endIndex = findPlaceholderEndIndex(result, startIndex);
			if (endIndex != -1) {
				String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
				String originalPlaceholder = placeholder;
				if (visitedPlaceholders == null) {
					visitedPlaceholders = new HashSet<>(4);
				}
				if (!visitedPlaceholders.add(originalPlaceholder)) {
					throw new IllegalArgumentException(
							"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
				}
				// 这里再次进行递归的调用解析占位符中的占位符 spring-${ ${username} }.xml
				// Recursive invocation, parsing placeholders contained in the placeholder key.
				placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
				// Now obtain the value for the fully resolved key...
				String propVal = placeholderResolver.resolvePlaceholder(placeholder);
				if (propVal == null && this.valueSeparator != null) {
					int separatorIndex = placeholder.indexOf(this.valueSeparator);
					if (separatorIndex != -1) {
						String actualPlaceholder = placeholder.substring(0, separatorIndex);
						String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
						propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
						if (propVal == null) {
							propVal = defaultValue;
						}
					}
				}
				if (propVal != null) {
					// Recursive invocation, parsing placeholders contained in the
					// previously resolved placeholder value.
					propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
					result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
					if (logger.isTraceEnabled()) {
						logger.trace("Resolved placeholder '" + placeholder + "'");
					}
					startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
				}
				else if (this.ignoreUnresolvablePlaceholders) {
					// Proceed with unprocessed value.
					startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
				}
				else {
					throw new IllegalArgumentException("Could not resolve placeholder '" +
							placeholder + "'" + " in value "" + value + """);
				}
				visitedPlaceholders.remove(originalPlaceholder);
			}
			else {
				startIndex = -1;
			}
		}
		return result.toString();
	}

image-20230626222002110

这里从解析到占位符内容 placeholder = “username ” 由 getPropertyAsRawString 方法处理获取最终的解析结果资源 Property

image-20230626222653907

protected String getPropertyAsRawString(String key) {
   return getProperty(key, String.class, false);
}
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
   if (this.propertySources != null) {
      for (PropertySource<?> propertySource : this.propertySources) {
         if (logger.isTraceEnabled()) {
            logger.trace("Searching for key '" + key + "' in PropertySource '" +
                  propertySource.getName() + "'");
         }
         Object value = propertySource.getProperty(key);
         if (value != null) {
            if (resolveNestedPlaceholders && value instanceof String) {
               value = resolveNestedPlaceholders((String) value);
            }
            logKeyFound(key, propertySource, value);
            return convertValueIfNecessary(value, targetValueType);
         }
      }
   }
   if (logger.isTraceEnabled()) {
      logger.trace("Could not find key '" + key + "' in any property source");
   }
   return null;
}

image-20230626223010246

最终获取占位符资源的具体对象内容 value = “peggy” ,最终返回解析后得到的具体 xml 文件名。

image-20230626223522850

image-20230626223603287

image-20230626223810867

refresh 核心步骤与流程

上面解析给定路径资源部分,完成了对于 xml 资源文件的定位,接下来就就是 spring 的核心 bean 的解析与加载,refresh 是也是整个spring 的主要方法,只要搞明白了 refresh 方法,就相当于明白了 spring 的整个 bean 的加载管理的处理流程。

AbstractApplicationContext.refresh

@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      /**
       * 1、设置容器的启动时间
       * 2、设置活跃状态为 true
       * 3、设置关闭状态为 false
       * 4、获取 Enveronment 对象,并且加载系统的属性值到 Enveronment 对象当中
       * 5、准备监听器和事件集合对象,默认为空的集合
       */
      //容器刷新前的准备工作(容器刷新的前戏)
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      //创建容器对象: DefaultListableBeanFactory
      //加载 xml 配置文件的属性值到当前工厂当中,并且获得 Bean 工厂,最重要的就是 BeanDefaction
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      // 设置 Bean 工厂的一些初始的属性值( BeanFactory 的前戏)
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         initMessageSource();

         // Initialize event multicaster for this context.
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         onRefresh();

         // Check for listener beans and register them.
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

image-20230626235022451

容器刷新的前置工作 prepareRefresh 方法
protected void prepareRefresh() {
    // Switch to active.
    // 设置容器的启动时间
    this.startupDate = System.currentTimeMillis();
    // 设置活跃状态为 true
    this.closed.set(false);
    // 设置关闭状态为 false
    this.active.set(true);

    //日志处理
    if (logger.isDebugEnabled()) {
        if (logger.isTraceEnabled()) {
            logger.trace("Refreshing " + this);
        }
        else {
            logger.debug("Refreshing " + getDisplayName());
        }
    }

    // Initialize any placeholder property sources in the context environment.
    // 留给子类覆盖,初始化属性资源(内部是空的留给用户自己扩展)
    initPropertySources();

    // Validate that all properties marked as required are resolvable:
    // see ConfigurablePropertyResolver#setRequiredProperties
    // 获取 Enveronment 对象,验证需要的属性文件是否已经放入系统当中
    getEnvironment().validateRequiredProperties();

    // Store pre-refresh ApplicationListeners...
    //判断刷新当前的应用程序监听器是否为空,如果为空,则将监听器添加到集合
    if (this.earlyApplicationListeners == null) {
        // applicationListenters 是留给扩展实现的(SpringBoot 项目当中这里的监听器会在初始化的时候注入)
        this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
    }
    else {
        // Reset local application listeners to pre-refresh state.
        //如果不等于空,清空监听器集合当中的对象
        this.applicationListeners.clear();
        this.applicationListeners.addAll(this.earlyApplicationListeners);
    }

    // Allow for the collection of early ApplicationEvents,
    // to be published once the multicaster is available...
    // 创建刷新前的监听事件集合
    this.earlyApplicationEvents = new LinkedHashSet<>();
}
获取当前的环境,属性校验

ConfigurableEnvironment.validateRequiredProperties

public ConfigurableEnvironment getEnvironment() {
    if (this.environment == null) {
        this.environment = createEnvironment();
    }
    return this.environment;
}

image-20230627000945467

image-20230627000845834

image-20230627001142957

image-20230627001217302

requiredProperties是通过setRequiredProperties方法设置的,保存在一个list里面,默认是空的,也就是不需要校验任何属性。

BeanFactory 创建
BeanFactory 创建

beanFactory 的创建由调用 AbstractApplicationContext.obtainFreshBeanFactory 实现。

//加载 xml 配置文件的属性值到当前工厂当中,并且获得 Bean 工厂,最重要的就是 BeanDefaction
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    //初始化 BeanFactory, 并进行 xml 文件的读取,并将得到的 BeanFactory 记录在当前的实体的属性中(可能在之前是存在 BeanFactroy 工厂的)
    refreshBeanFactory();
    return getBeanFactory();
}
protected final void refreshBeanFactory() throws BeansException {
    //判断工厂是否存在
    if (hasBeanFactory()) {
        //销毁之前的工厂
        destroyBeans();
        //关闭工厂
        closeBeanFactory();
    }
    try {
        //创建一个新的 DefaultListableBeanFactory 对象
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        // 1. 设置属性值(为了序列化指定 id 可以从 id 反序列化到 BeanFactory 对象)
        beanFactory.setSerializationId(getId());
        // 2. 加载 xml 文件的一些配置(定制 beanFactory 设置相关属性, 包括是否允许覆盖同名称的不同定义的对象以及循环依赖)
        customizeBeanFactory(beanFactory);
        // 3. 初始化 domcumentReader , 并进行 xml 文件读取解析
        loadBeanDefinitions(beanFactory);
        this.beanFactory = beanFactory;
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}
BeanFactory 接口

此接口实际上就是一个 Bean 容器,其继承关系的体系:

BeanFactory

创建 DefaultListableBeanFactory 对象
protected DefaultListableBeanFactory createBeanFactory() {
    return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}

AbstartApplicationContext.getInternalParentBeanFactory

从内部获取一个 BeanFactory 对象,这里的 BeanFactory 是为初始化不存在的为 Null

protected BeanFactory getInternalParentBeanFactory() {
	return (getParent() instanceof ConfigurableApplicationContext ?
			((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent());
}

创建 DefalultListableBeanFactory 对象,调用父类构造

public DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
    super(parentBeanFactory);
}

创建 AbstractAutwireCapableBeanFactory

public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
    this();
    setParentBeanFactory(parentBeanFactory);
}

父类构造调用无参构造,设置需要忽略依赖接口

public AbstractAutowireCapableBeanFactory() {
    super();
    //忽略要依赖的接口
    ignoreDependencyInterface(BeanNameAware.class);
    ignoreDependencyInterface(BeanFactoryAware.class);
    ignoreDependencyInterface(BeanClassLoaderAware.class);
}

在 ignoreDependencyInterface 当中设置的接口都是都是以 * Aware 结尾,在首页的关于 ClassPathApplicationContext 中的 UML 关系图中有提到过关于 << Aware >> 接口。

AbstractAutwireCapableBeanFactory.ignoreDependencyInterface 方法实际上是将这些类的对象添加到了集合当中,这也印证了AbstractAutwireCapableBeanFactory 的子类DefalultListableBeanFactory为什么是 Listable 其对象本身就是一个大的容器盒。

image-20230630165515997

关于该接口的作用官方的解释如下:

额~~~ 这一块我也没搞懂,后续有新的了解再做其补充

A marker superinterface indicating that a bean is eligible to be notified by the Spring container of a particular framework object through a callback-style method. The actual method signature is determined by individual subinterfaces but should typically consist of just one void-returning method that accepts a single argument.

AbstractAutowireCapableBeanFactory.setParentBeanFactory

@Override
public void setParentBeanFactory(@Nullable BeanFactory parentBeanFactory) {
    if (this.parentBeanFactory != null && this.parentBeanFactory != parentBeanFactory) {
        throw new IllegalStateException("Already associated with parent BeanFactory: " + this.parentBeanFactory);
    }
    if (this == parentBeanFactory) {
        throw new IllegalStateException("Cannot set parent bean factory to self");
    }
    this.parentBeanFactory = parentBeanFactory;
}

由于之前从 AbstartApplicationContext.getInternalParentBeanFactory 获取的内部 BeanFactory 是空的,因此这里传入的 parentBeanFactory 是一个空父类 Bean 工厂对象。

image-20230630163946285

直接返回一个新的对象 DefaultListableBeanFactory 对象。关于父类的 DefaltListableBeanFactory 的父类 AbstractAutowireCapableBeanFactory 中的 BeanFactory 对象 ParentBeanFactory 对象具体是用来干什么的,为什么需要在创建 DefaultListableBeanFactory 对象的时候,需要从内部获取一次 parentBeanFactory ,这里一个可以需要后续的知识储备才能解答叭。

image-20230630164356573

为 beanFactory 设置序列化 ID
// 1. 设置属性值(为了序列化指定 id 可以从 id 反序列化到 BeanFactory 对象)
beanFactory.setSerializationId(getId());
@Override
public String getId() {
    return this.id;
}
/**
	 * Specify an id for serialization purposes, allowing this BeanFactory to be
	 * deserialized from this id back into the BeanFactory object, if needed.
	 */
public void setSerializationId(@Nullable String serializationId) {
    if (serializationId != null) {
        serializableFactories.put(serializationId, new WeakReference<>(this));
    }
    else if (this.serializationId != null) {
        serializableFactories.remove(this.serializationId);
    }
    this.serializationId = serializationId;
}

AbstractApplicationContext.getId

//创建上下文的唯一标识
/** Unique id for this context, if any. */
private String id = ObjectUtils.identityToString(this);

这里的 this.id 的初始化时机在 AbstractApplicationContext 被创建的时候所复制得到的,而 AbstractApplicationContext 对象的创建时机在 ClassPathXmlApplicationContext 构造方法中多次调用父类构造完成初始化。

image-20230630170950355

BeanFactory 的定制

在 AbstractRefreshableApplicationContext.customizeBeanFactory 的方法当中为我们自己继承的 MyCalssPathXmlApplication 中通过对继承的 ClassPathXmlApplication 类中的 customizeBeanFactory(DefaultListableBeanFactory beanFactory)方法进行重写。

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    //如果属性 allowBeanDefinitionOverrding 不为空, 设置 beanFactory 对象相应的属性值,是否允许覆盖同名称的不同定义的对象
    /**
		 * 这里的 allowBeanDefintionOverriding 与 allowCircularReferences 默认在父类的构造方法中赋值为 true
		 * 而这里的值,我们可以在自己定义继承的类 MyClassPathXmlApplication 中通过对继承的 ClassPathXmlApplication 类中的
		 * customizeBeanFactory(DefaultListableBeanFactory beanFactory) 方法进行重写
		 */
    if (this.allowBeanDefinitionOverriding != null) {
        beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    if (this.allowCircularReferences != null) {
        beanFactory.setAllowCircularReferences(this.allowCircularReferences);
    }
}

自定义实现 customizeBeanFactory 方法

public class MyClassPathXmlApplicationContext(){
    @Override
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory)		{
        super.customizeBeanFactory(beanFactory);
        System.out.println("调用了自定义的方法")
    }
}
public class Test{
    public static void main(String[]  args){
        ApplicationContext context=new MyClassPathXmlApplicationContext("spring-${username}.xml");
    }
}

image-20230702211703344

解析 xml 初始化 domcumentReader

AbstractXmlApplicationContext.loadBeanDefinitions 该方法当中就完成了,xml 文件的解析工作,以及对于 bean 的初始化加载。

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // Create a new XmlBeanDefinitionReader for the given BeanFactory.
    // 创建一个 xml 的 beanDefinitionReader , 并通过回调设置到 BeanFactory 中
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // Configure the bean definition reader with this context's
    // resource loading environment.
    // 给 reader 对象设置环境对象
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    // Allow a subclass to provide custom initialization of the reader,
    // then proceed with actually loading the bean definitions.
    // 初始化 beanDefinitionReader 对象 , 此处设置配置文件是否要进行验证
    initBeanDefinitionReader(beanDefinitionReader);
    // 开始完成 beanDefinition 的加载
    loadBeanDefinitions(beanDefinitionReader);
}

AbstractXmlApplcationContext.loaderBeanDefinitons

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    //以为 Resource[] 数组形式的配置文件路径
    //ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, @Nullable ApplicationContext parent)
	Resource[] configResources = getConfigResources();
	if (configResources != null) {
		reader.loadBeanDefinitions(configResources);
	}
    //以为字符串数组形式的配置文件路径
	String[] configLocations = getConfigLocations();
	if (configLocations != null) {
		reader.loadBeanDefinitions(configLocations);
	}
}
protected String[] getConfigLocations() {
    return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
}

image-20230702222903086

AbstractBeanDefactionReader.loadBeanDefinitions

public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
	Assert.notNull(locations, "Location array must not be null");
	int count = 0;
	for (String location : locations) {
		count += loadBeanDefinitions(location);
	}
	return count;
}
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
	return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> 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 {
            //调用 DefinitionResourxesLoader 的 getResources 完成具体的 Resource 的定位
            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 {
        // Can only load single resources by absolute URL.
        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;
    }
}