0xTTEPX

Just do it, deeply...

Follow me on GitHub

ApplicationContext接口定义

write by donaldhan, 2017-12-19 10:30

引言

ClassPathXmlApplicationContext声明

上一篇文中我们,我们看了ClassPathXmlApplicationContext声明,并整理出ClassPathXmlApplicationContext的类图,ClassPathXmlApplicationContext直接或间接地实现了 EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourceLoader,Lifecycle,Closeable,BeanNameAware,InitializingBean,DisposableBean 。今天我们先来看一应用上下文ApplicationContext接口及其父接口的定义。

ClassPathXmlApplicationContext

目录

父接口定义

我们先来看一下 BeanFactory 两个子类接口 ListableBeanFactory,HierarchicalBeanFactory 的定义,看之前,先看BeanFactory接口定义:

BeanFactory

BeanFactory接口源码参见BeanFactory

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;

/**
 * <p>Bean factory implementations should support the standard bean lifecycle interfaces
 * as far as possible. The full set of initialization methods and their standard order is:
 * 尽可能地bean工厂的实现,应该支持标准的bean生命周期接口,比如lifecycle。所有初始化方法和他们的标准顺序如下:
 * <ol>
 * <li>BeanNameAware's {@code setBeanName}
 * <li>BeanClassLoaderAware's {@code setBeanClassLoader}
 * <li>BeanFactoryAware's {@code setBeanFactory}
 * <li>EnvironmentAware's {@code setEnvironment}
 * <li>EmbeddedValueResolverAware's {@code setEmbeddedValueResolver}
 * <li>ResourceLoaderAware's {@code setResourceLoader}
 * (only applicable when running in an application context)
 * <li>ApplicationEventPublisherAware's {@code setApplicationEventPublisher}
 * (only applicable when running in an application context)
 * <li>MessageSourceAware's {@code setMessageSource}
 * (only applicable when running in an application context)
 * <li>ApplicationContextAware's {@code setApplicationContext}
 * (only applicable when running in an application context)
 * <li>ServletContextAware's {@code setServletContext}
 * (only applicable when running in a web application context)
 * <li>{@code postProcessBeforeInitialization} methods of BeanPostProcessors
 * <li>InitializingBean's {@code afterPropertiesSet}
 * <li>a custom init-method definition
 * <li>{@code postProcessAfterInitialization} methods of BeanPostProcessors
 * </ol>
 *
 * <p>On shutdown of a bean factory, the following lifecycle methods apply:
 * <ol>在关闭bean工厂之后,下面方法回调用
 * <li>{@code postProcessBeforeDestruction} methods of DestructionAwareBeanPostProcessors
 * <li>DisposableBean's {@code destroy}
 * <li>a custom destroy-method definition
 * </ol>
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 13 April 2001
 * @see BeanNameAware#setBeanName
 * @see BeanClassLoaderAware#setBeanClassLoader
 * @see BeanFactoryAware#setBeanFactory
 * @see org.springframework.context.ResourceLoaderAware#setResourceLoader
 * @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher
 * @see org.springframework.context.MessageSourceAware#setMessageSource
 * @see org.springframework.context.ApplicationContextAware#setApplicationContext
 * @see org.springframework.web.context.ServletContextAware#setServletContext
 * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization
 * @see InitializingBean#afterPropertiesSet
 * @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName
 * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization
 * @see DisposableBean#destroy
 * @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName
 */
public interface BeanFactory {
	String FACTORY_BEAN_PREFIX = "&";
	Object getBean(String name) throws BeansException;
	<T> T getBean(String name, Class<T> requiredType) throws BeansException;
	boolean containsBean(String name);
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;
	String[] getAliases(String name);

}

从上面可以看出,BeanFactory接口主要是主要提供了根据名称或类型获取bean的相关方法,以及判断bean是否匹配指定类型或判断 是否包含指定name的共享单实例或多实例bean。需要注意的是,如果bean工厂的实现是可继承工厂 HierarchicalBeanFactory,那么调用这些方法,如果没有在当前bean工厂实例中找到,将会从父工厂中查到。另外还需要注意一点,判断一个bean是否为共享单例模式,可以使用isSingleton方法,返回true,即是,返回false,并不能表示bean是多实例bean,具体要用isPrototype方法判断,同理isPrototype方法也是如此。

再来看ListableBeanFactory接口的定义:

ListableBeanFactory

具体ListableBeanFactory的源码,参见ListableBeanFactory

package org.springframework.beans.factory;
import java.lang.annotation.Annotation;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;

/**
 *ListableBeanFactory扩展了bean工厂接口,ListableBeanFactory接口的实现可以列举所有bean实例,
 *而不是作为一个请求客户端尝试以bean的名称,搜索bean。BeanFactory实现将会预先加载所有配置中BeanFactory
 *的所有bean定义。
 *如果bean工厂是一个可继承的bean工厂HierarchicalBeanFactory,返回值将不会考虑父类工厂bean,
 *仅仅考虑与当前bean工厂关联的bean。BeanFactoryUtils工具类获取的祖先bean工厂,将考虑在内。
 *接口方法,只会搜索当前bean工厂的bean定义。将会忽略所有通过ConfigurableBeanFactory的registerSingleton方法,
 *注册到bean工厂的单例模式bean。
 *注意:getBeanDefinitionCount和containsBeanDefinition方法异常情况,此方法不是设计为频繁调用的。
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since 16 April 2001
 * @see HierarchicalBeanFactory
 * @see BeanFactoryUtils
 */
public interface ListableBeanFactory extends BeanFactory {
	boolean containsBeanDefinition(String beanName);
	int getBeanDefinitionCount();
	String[] getBeanDefinitionNames();
	String[] getBeanNamesForType(ResolvableType type);
	String[] getBeanNamesForType(Class<?> type);
	String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
	<T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException;
	<T> Map<String, T> getBeansOfType(Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
			throws BeansException;
	String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
	Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
	<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
			throws NoSuchBeanDefinitionException;
}

从ListableBeanFactory的定义,可以看出ListableBeanFactory接口主要提供了判断是否存在给定name的bean定义,获取bean定义数量,获取指定类型的bean定义的name集或name与bean实例的映射集,获取待指定注解的bean定义的name或name与bean实例的映射集,以及获取给定name对应的bean的注定注解实例。需要注意的是,提供的操作不会到可继承bean工厂中去搜索,但包括BeanFactoryUtils工具类获取bean工厂的祖先bean工厂。另外getBeanNamesForType和getBeanNamesForAnnotation方法可以通过includeNonSingletons和allowEagerInit, 控制搜索bean的作用域范围和是否初始化懒加载单例模式bean与工厂bean。

再来看HierarchicalBeanFactory接口的定义:

HierarchicalBeanFactory

具体HierarchicalBeanFactory源码,参见[HierarchicalBeanFactory][] [HierarchicalBeanFactory]:https://github.com/Donaldhan/spring-framework/blob/4.3.x/spring-beans/src/main/java/org/springframework/beans/factory/HierarchicalBeanFactory.java “HierarchicalBeanFactory”

package org.springframework.beans.factory;

/**
 *HierarchicalBeanFactory作为bean工厂的实现,标志着为一个可继承的bean工厂。
 *在ConfigurableBeanFactory接口的setParentBeanFactory方法,中可以配置bean工厂的父类工厂。
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since 07.07.2003
 * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#setParentBeanFactory
 */
public interface HierarchicalBeanFactory extends BeanFactory {
	BeanFactory getParentBeanFactory();
	boolean containsLocalBean(String name);
}

从HierarchicalBeanFactory的定义来看,HierarchicalBeanFactory接口标志一个以可继承bean工厂,我们可以通过 ConfigurableBeanFactory 接口的 setParentBeanFactory 方法配置bean工厂的父类工厂,主要提供获取父工厂操作,以及判断在本地bean工厂中是否存在指定name对应的bean的操作,但忽略祖先上下文中的bean定义。

ApplicationEventPublisher

ApplicationEventPublisher的源码,参见ApplicationEventPublisher

package org.springframework.context;

/**
 *ApplicationEventPublisher接口,封装了事件发布功能,作为ApplicationContext的父接口,
 *服务于应用上下文ApplicationContext。
 * @author Juergen Hoeller
 * @author Stephane Nicoll
 * @since 1.1.1
 * @see ApplicationContext
 * @see ApplicationEventPublisherAware
 * @see org.springframework.context.ApplicationEvent
 * @see org.springframework.context.event.EventPublicationInterceptor
 */
public interface ApplicationEventPublisher {
	void publishEvent(ApplicationEvent event);
	void publishEvent(Object event);

}

从上面可看出,事件发布接口ApplicationEventPublisher,主要作为ApplicationContext的父接口,封装了事件发布功能,提供了事件发布功能。当事件发布时,通知所有注册到当前应用关注ApplicationEvent事件event的监听器,如果发布的事件不是 ApplicationEvent, 则将会包装成 PayloadApplicationEvent

ApplicationEvent

再来简单看一下应用事件ApplicationEvent

package org.springframework.context;

import java.util.EventObject;

/**
 *所有的应用事件,将会继承此类。抽象不意味着,一般的应用事件不可以直接发布。
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
public abstract class ApplicationEvent extends EventObject {
	/** use serialVersionUID from Spring 1.2 for interoperability */
	private static final long serialVersionUID = 7099057708183571937L;
	/** System time when the event happened 事件发生的系统时间*/
	private final long timestamp;
	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}
	public final long getTimestamp() {
		return this.timestamp;
	}

}

从上面可以看出,ApplicationEvent用于表示应用发生的事件,事件包括事件发生的时间和事件发生源。

我们再来看一下ApplicationEvent的父类EventObject,EventObject属于jdk的范畴。

package java.util;

/**
 * <p>
 * The root class from which all event state objects shall be derived.
 所有事件状态对象的原始类。
 * <p>
 * All Events are constructed with a reference to the object, the "source",
 * that is logically deemed to be the object upon which the Event in question
 * initially occurred upon.
 所有事件构造有一个对象已用,此对象用于表示,事件发生源。
 *
 * @since JDK1.1
 */

public class EventObject implements java.io.Serializable {

    private static final long serialVersionUID = 5516075349620653480L;

    /**
     * The object on which the Event initially occurred.
     事件发生源
     */
    protected transient Object  source;

    /**
     * Constructs a prototypical Event.
     *
     * @param    source    The object on which the Event initially occurred.
     * @exception  IllegalArgumentException  if source is null.
     */
    public EventObject(Object source) {
        if (source == null)
            throw new IllegalArgumentException("null source");

        this.source = source;
    }
    public Object getSource() {
        return source;
    }
    ...
}

从上面来看EventObject,表示一个事件对象,同时记录事件发生源。

ApplicationListener

再来看一下应用监听器ApplicationListener

package org.springframework.context;

import java.util.EventListener;

/**
 * Interface to be implemented by application event listeners.
 * Based on the standard {@code java.util.EventListener} interface
 * for the Observer design pattern.。
 *所有应用事件监听器将实现ApplicationListener接口。此接口基于标准的观察者模式的接口java.util.EventListener。
 * <p>As of Spring 3.0, an ApplicationListener can generically declare the event type
 * that it is interested in. When registered with a Spring ApplicationContext, events
 * will be filtered accordingly, with the listener getting invoked for matching event
 * objects only.
 * 从spring3.0以后,应用监听器需要声明关注的应用事件类型。当监听器注册到spring的应用上下文ApplicationContext时,
 * 将会根据事件类型过滤监听器,匹配事件类型的监听器才会被通知。
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @param <E> the specific ApplicationEvent subclass to listen to
 * @see org.springframework.context.event.ApplicationEventMulticaster
 */
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	/**
	 * Handle an application event.
	 * 处理应用事件
	 * @param event the event to respond to
	 */
	void onApplicationEvent(E event);

}

从上面可以看出,监听器接口ApplicationListener,是基于标准JDK的观察者模式的接口java.util.EventListener, 从spring3.0以后,应用监听器需要声明关注的应用事件类型。当监听器注册到spring的应用上下文ApplicationContext时, 将会根据事件类型过滤监听器,匹配事件类型的监听器才会被通知。接口主要提供了处理事件操作。

来简单看一下jdk的事件监听器接口的声明:

package java.util;

/**
 * A tagging interface that all event listener interfaces must extend.
 * @since JDK1.1
 */
public interface EventListener {
}

从上面可以看出事件监听器接口,从JDK1.1,就已经出现。

我们接着看EnvironmentCapable接口

EnvironmentCapable

源码参见EnvironmentCapable

package org.springframework.core.env;

/**
 * EnvironmentCapable表示一个组件包括一个或暴露一个Environment环境引用。
 *Spring的所有应用上下文都是EnvironmentCapable接口实现,用于应用上下文与环境交互。
 * 需要注意的是,ApplicationContext扩展了EnvironmentCapable接口,通过getEnvironment方法暴露环境配置;
 * 然而ConfigurableApplicationContext将会重定义getEnvironment方法,返回一个ConfigurableEnvironment。
 * 两种方法带来的效果是,在环境配置Environment对象在ConfigurableApplicationContext可访问以前,都是自读的,
 * 可以理解为ConfigurableApplicationContext的getEnvironment方法返回的环境对象时可修改的。
 *
 * @author Chris Beams
 * @since 3.1
 * @see Environment
 * @see ConfigurableEnvironment
 * @see org.springframework.context.ConfigurableApplicationContext#getEnvironment()
 */
public interface EnvironmentCapable {

	/**
	 * Return the {@link Environment} associated with this component
	 * (may be {@code null} or a default environment).
	 * 返回组件关联的环境Environment,没有则为空。
	 */
	Environment getEnvironment();

}

从上面可以看出,接口从spring3.1才使用,EnvironmentCapable接口,表示包括或暴露一个Environment环境引用的组件。Spring的所有应用上下文都是EnvironmentCapable接口实现,用于应用上下文与环境交互。需要注意的是,ApplicationContext扩展了EnvironmentCapable接口,通过getEnvironment方法暴露环境配置;然而 ConfigurableApplicationContext 将会重定义getEnvironment方法,返回一个ConfigurableEnvironment。 两种方法带来的效果是,在环境配置Environment对象在 ConfigurableApplicationContext 可访问以前,都是自读的,可以理解为 ConfigurableApplicationContext 的getEnvironment方法返回的环境象时可修改的。接口提供了获取环境配置操作。

我们再来看一下环境配置Environment接口的定义:

package org.springframework.core.env;

/**
 *Environment接口表示当前应用正在运行的环境。应用环境的配置有两个方面:配置profiles与属性properties,属性相关的方法,
 *通过Environment的父接口暴露属性访问方法。
 *命名的配置profile,在配置激活的情况下,注册到容器的bean定义将会根据配置profile进行逻辑地分组。无论一个配置已通过xml或注解进行配置,
 *没有bean都属于一个配置;具体参数spring-beans 3.1 的shema和@Profile注解的说明。可以通过getDefaultProfiles和getActiveProfiles方法
 *来确定环境与配置的对象的关联关系。
 * 属性配置在所有应用中,扮演者一个重要的角色,可以有不同种类的属性源:比如属性文件,java虚拟机系统属性,系统环境变量,JNDI,
 * servlet上下文配置,ad-hoc属性对象,Map等。关联属性的环境对象,提供配置属性源和解决属性的一个方便的配置接口。
 *
 * 为了查询配置的状态或解决属性,应用上下文管理的bean,也许通过EnvironmentAware或依赖环境Environment的注解@Inject,注册到应用上下文。
 *在大多说的情况下,应用层的bean不需要与环境直接进行交互,但需要使用属性placeholder配置器,配置替换形式如“${...}”
 *的属性,比如PropertySourcesPlaceholderConfigurer,本身即是一个EnvironmentAware,从spring3.1以后,
 *当使用<context:property-placeholder/>配置属性配置器时,默认的配置将会配置激活。
 * 环境配置对象必须通过ConfigurableEnvironment接口进行配置,所有AbstractApplicationContext的子类,都可通过getEnvironment方法
 * 返回一个可配置环境接口ConfigurableEnvironment。
 *
 * @author Chris Beams
 * @since 3.1
 * @see PropertyResolver
 * @see EnvironmentCapable
 * @see ConfigurableEnvironment
 * @see AbstractEnvironment
 * @see StandardEnvironment
 * @see org.springframework.context.EnvironmentAware
 * @see org.springframework.context.ConfigurableApplicationContext#getEnvironment
 * @see org.springframework.context.ConfigurableApplicationContext#setEnvironment
 * @see org.springframework.context.support.AbstractApplicationContext#createEnvironment
 */
public interface Environment extends PropertyResolver {

	/**
	 * 返回当前环境显示激活的配置集。配置用于创建有条件地注册bean定义的逻辑分组,比如基于开发环境的配置。配置可以通过设置系统属性
	 * {@linkplain AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
	 * "spring.profiles.active"}或者调用ConfigurableEnvironment#setActiveProfiles(String...)方法配置。
	 * 如果没有配置显示激活, #getDefaultProfiles()返回的默认配置将会被自动激活。
	 * @see #getDefaultProfiles
	 * @see ConfigurableEnvironment#setActiveProfiles
	 * @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
	 */
	String[] getActiveProfiles();

	/**
	 * 当没有配置显示激活, 返回的默认将会被自动激活的配置集。
	 * @see #getActiveProfiles
	 * @see ConfigurableEnvironment#setDefaultProfiles
	 * @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
	 */
	String[] getDefaultProfiles();

	/**
	 * 判断一个或多个配置是否激活,或者在默认显示激活的配置情况下,一个或多个配置是否在默认的配置集。如果配置以'!'逻辑符开头,
	 * 表示当对应的配置没有开启时,返回true,比如env.acceptsProfiles("p1", "!p2"),如果'p1'激活,"!p2"没有激活,
	 * 将返回true。如果调用时0个参数,或者有一个配置为null,或空字符串等,将会抛出非法参数异常
	 * @see #getActiveProfiles
	 * @see #getDefaultProfiles
	 */
	boolean acceptsProfiles(String... profiles);

}

从spring3.1开始,才出现Environment接口,Environment接口同时是一个 PropertyResolver 接口,提供了获取激活配置 Profiles 和默认配置的操作,同时提供了判断配置是否激活操作。应用环境Environment有一个或多个配置 Profiles,配置可以理解为配置集或类型,比如开发,测试,体验,生产等环境。当应用存在需要引用属性的情况,我们可以从环境中获取,应为环境是一个 PropertyResolver。环境配置对象必须通过ConfigurableEnvironment接口进行配置,所有AbstractApplicationContext的子类,都可通过getEnvironment方法返回一个可配置环境接口ConfigurableEnvironment。

PropertyResolver

我们简单看一下PropertyResolver接口

package org.springframework.core.env;

/**
 * PropertyResolver是一个依赖于底层数据解决属性接口
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.1
 * @see Environment
 * @see PropertySourcesPropertyResolver
 */
public interface PropertyResolver {

	/**
	 * 如果给定的key不为null,返回是否包含给定属性key
	 * i.e. if the value for the given key is not {@code null}.
	 */
	boolean containsProperty(String key);

	/**
	 * 返回给定属性key的值,没有为null
	 * @param key the property name to resolve
	 * @see #getProperty(String, String)
	 * @see #getProperty(String, Class)
	 * @see #getRequiredProperty(String)
	 */
	String getProperty(String key);

	/**
	 * 返回给定属性key的值,没有返回默认值defaultValue
	 * @param key the property name to resolve
	 * @param defaultValue the default value to return if no value is found
	 * @see #getRequiredProperty(String)
	 * @see #getProperty(String, Class)
	 */
	String getProperty(String key, String defaultValue);

	/**
	 * 返回是给定类型属性key的值,没有为null
	 * @param key the property name to resolve
	 * @param targetType the expected type of the property value
	 * @see #getRequiredProperty(String, Class)
	 */
	<T> T getProperty(String key, Class<T> targetType);

	/**
	 * 返回是给定类型属性key的值,没有返回默认值defaultValue
	 * @param key the property name to resolve
	 * @param targetType the expected type of the property value
	 * @param defaultValue the default value to return if no value is found
	 * @see #getRequiredProperty(String, Class)
	 */
	<T> T getProperty(String key, Class<T> targetType, T defaultValue);

	/**
	 * 返回是给定类型属性key的值,如果值得类型不同,则进行转换,转换异常,则抛出ConversionException,没有对应的值,则返回null
	 * @see #getProperty(String, Class)
	 * @deprecated as of 4.3, in favor of {@link #getProperty} with manual conversion
	 * to {@code Class} via the application's {@code ClassLoader}
	 */
	@Deprecated
	<T> Class<T> getPropertyAsClass(String key, Class<T> targetType);

	/**
	 * 返回是给定属性key的值,没有抛出IllegalStateException
	 * @throws IllegalStateException if the key cannot be resolved
	 * @see #getRequiredProperty(String, Class)
	 */
	String getRequiredProperty(String key) throws IllegalStateException;

	/**
	 * 返回是给定类型属性key的值,没有抛出IllegalStateException
	 * @throws IllegalStateException if the given key cannot be resolved
	 */
	<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;

	/**
	 * 在给定上文本中,替换引用属性“${...}”,没有默认的情况下,则忽略,不做任何改变
	 * @param text the String to resolve
	 * @return the resolved String (never {@code null})
	 * @throws IllegalArgumentException if given text is {@code null}
	 * @see #resolveRequiredPlaceholders
	 * @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String)
	 */
	String resolvePlaceholders(String text);

	/**
	 * 此方法,与上面方法不同的是,没有匹配的值,则抛出异常IllegalArgumentException
	 * @return the resolved String (never {@code null})
	 * @throws IllegalArgumentException if given text is {@code null}
	 * or if any placeholders are unresolvable
	 * @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String, boolean)
	 */
	String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;

}

从上面可以看出从spring3.1开始,PropertyResolver才出现,PropertyResolver注意根据属性源,是否包含给定属性,获取相关属性的值及获取给定类型属性的值操作,同时提供了替换给定上文本中的引用属性“${…}”操作。

ResourcePatternResolver

具体源码参见:ResourcePatternResolver


package org.springframework.core.io.support;

import java.io.IOException;

import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

/**
 * Strategy interface for resolving a location pattern (for example,
 * an Ant-style path pattern) into Resource objects.
 *ResourcePatternResolver接口将指定路径下的文件加载资源对象。
 * <p>This is an extension to the {@link org.springframework.core.io.ResourceLoader}
 * interface. A passed-in ResourceLoader (for example, an
 * {@link org.springframework.context.ApplicationContext} passed in via
 * {@link org.springframework.context.ResourceLoaderAware} when running in a context)
 * can be checked whether it implements this extended interface too.
 *接口拓展了org.springframework.core.io.ResourceLoader接口。
 * <p>{@link PathMatchingResourcePatternResolver} is a standalone implementation
 * that is usable outside an ApplicationContext, also used by
 * {@link ResourceArrayPropertyEditor} for populating Resource array bean properties.
 *PathMatchingResourcePatternResolver接口是ResourcePatternResolver的一个独立是吸纳,不能在ApplicationContext外部使用,
 *可以使用ResourceArrayPropertyEditor,设置bean的属性。
 * <p>Can be used with any sort of location pattern (e.g. "/WEB-INF/*-context.xml"):
 * Input patterns have to match the strategy implementation. This interface just
 * specifies the conversion method rather than a specific pattern format.
 *可以用于任何种类的位置模式(e.g. "/WEB-INF/*-context.xml"):输入的模式必须匹配指定的规则。
 * <p>This interface also suggests a new resource prefix "classpath*:" for all
 * matching resources from the class path. Note that the resource location is
 * expected to be a path without placeholders in this case (e.g. "/beans.xml");
 * JAR files or classes directories can contain multiple files of the same name.
 *此接口建议以 "classpath*:"为前缀,创建一个匹配class路径的所有资源。需要注意的是,资源位置应该为一个路径,而不是没有占位符,
 *比如 (e.g. "/beans.xml");jar包文件或多个相同名字的文件。
 * @author Juergen Hoeller
 * @since 1.0.2
 * @see org.springframework.core.io.Resource
 * @see org.springframework.core.io.ResourceLoader
 * @see org.springframework.context.ApplicationContext
 * @see org.springframework.context.ResourceLoaderAware
 */
public interface ResourcePatternResolver extends ResourceLoader {

	/**
	 * Pseudo URL prefix for all matching resources from the class path: "classpath*:"
	 * This differs from ResourceLoader's classpath URL prefix in that it
	 * retrieves all matching resources for a given name (e.g. "/beans.xml"),
	 * for example in the root of all deployed JAR files.
	 * 所有匹配class路径的伪URL前缀“classpath*:”。以URL前缀“classpath*的开头的class路径资源,
	 * 与匹配给定name("/beans.xml")的ResourceLoader的以"classpath:"为前缀的资源不同,比如,JAR包的根目录。
	 * @see org.springframework.core.io.ResourceLoader#CLASSPATH_URL_PREFIX
	 */
	String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

	/**
	 * Resolve the given location pattern into Resource objects.
	 * <p>Overlapping resource entries that point to the same physical
	 * resource should be avoided, as far as possible. The result should
	 * have set semantics.
	 * 将给定路径模式下的文件,转换成资源Resource对应。尽量避免在同级物理层环境,出现重叠的资源entries。
	 * 返回的结果为一个资源集。
	 * @param locationPattern the location pattern to resolve
	 * @return the corresponding Resource objects
	 * @throws IOException in case of I/O errors
	 */
	Resource[] getResources(String locationPattern) throws IOException;

}

从上,可以看出,ResourcePatternResolver拓展了 ResourceLoader 接口,主要用于解决或加载给定路径下的资源文件,ResourcePatternResolver建议使用 以 “classpath*:”为前缀,创建一个匹配class路径的所有资源。

为了理解下面这个属性

String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

,我们来看ResourceLoader

public interface ResourceLoader {

	/** Pseudo URL prefix for loading from the class path: "classpath:" */
	String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
    ...
}
public abstract class ResourceUtils {

	/** Pseudo URL prefix for loading from the class path: "classpath:" */
	public static final String CLASSPATH_URL_PREFIX = "classpath:";
    ...
}

从上面可以看出,ResourcePatternResolver建议使用以 classpath: 为前缀,创建一个匹配class路径的所有资源,当然也可以是其他形式的位置模式。ResourceLoader的资源以 *classpath: 为前缀,这两种方式的不同,我们在看完ResourceLoader接口的定义,再来比较。

ResourceLoader

具体源码参见:ResourceLoader

package org.springframework.core.io;

import org.springframework.util.ResourceUtils;

/**
 * Strategy interface for loading resources (e.. class path or file system
 * resources). An {@link org.springframework.context.ApplicationContext}
 * is required to provide this functionality, plus extended
 * {@link org.springframework.core.io.support.ResourcePatternResolver} support.
 *ResourceLoader是一个策略接口,用于加载资源(比如class路径或文件系统资源)。org.springframework.context.ApplicationContext
 *需要提供这些功能,同时要拓展ResourcePatternResolver的支持。
 * <p>{@link DefaultResourceLoader} is a standalone implementation that is
 * usable outside an ApplicationContext, also used by {@link ResourceEditor}.
 *默认资源加载器DefaultResourceLoader是一个独立的实现,可以在在应用上下文外部使用。也可以通过ResourceEditor使用。
 * <p>Bean properties of type Resource and Resource array can be populated
 * from Strings when running in an ApplicationContext, using the particular
 * context's resource loading strategy.
 * 当运行在应用上下文中,可以使用特殊的上下为资源加载策略,类型资源和资源数组可以从字符串构建bean的属性。
 * @author Juergen Hoeller
 * @since 10.03.2004
 * @see Resource
 * @see org.springframework.core.io.support.ResourcePatternResolver
 * @see org.springframework.context.ApplicationContext
 * @see org.springframework.context.ResourceLoaderAware
 */
public interface ResourceLoader {

	/** Pseudo URL prefix for loading from the class path: "classpath:" */
	String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;

	/**
	 * 返回特殊位置资源的资源句柄。
	 * 资源句柄可以总是可以重用资源描述,允许多次调用Resource#getInputStream()方法获取资源输入流。
	 * 必须支持完全限定路径,如"file:C:/test.dat".
	 * <li>Must support classpath pseudo-URLs, e.g. "classpath:test.dat".
	 * 必须支持伪类路径URL,比如e.g. "classpath:test.dat".
	 * 应该支持相对路径的文件路径,比如"WEB-INF/test.dat".
	 * 这些需要具体的实现,典型的通过ApplicationContext提供实现
	 * 注意:一个资源handle不以为者,资源存在,可以调用Resource#exists检查资源是否存在。
	 * @param location the resource location
	 * @return a corresponding Resource handle (never {@code null})
	 * @see #CLASSPATH_URL_PREFIX
	 * @see Resource#exists()
	 * @see Resource#getInputStream()
	 */
	Resource getResource(String location);

	/**
	 * 暴露资源加载器ResourceLoader使用的类加载器。
	 * 客户端需要直接访问ClassLoader,可以使用ResourceLoader的统一管理器。
	 * @return the ClassLoader (only {@code null} if even the system
	 * ClassLoader isn't accessible)
	 * 如果系统类加载器不可访问,则返回null
	 * @see org.springframework.util.ClassUtils#getDefaultClassLoader()
	 */
	ClassLoader getClassLoader();

}

从上面可以看出,ResourceLoader接口用于加载资源class路径或文件系统等类型资源,提供获取给定位置的资源操作和获取系统ClassLoader。 再来简单看一下ClassUtils,获取类加载器

package org.springframework.util;

import java.beans.Introspector;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/**
 * Miscellaneous class utility methods.
 * Mainly for internal use within the framework.
 *
 * @author Juergen Hoeller
 * @author Keith Donald
 * @author Rob Harrop
 * @author Sam Brannen
 * @since 1.1
 * @see TypeUtils
 * @see ReflectionUtils
 */
public abstract class ClassUtils {
public static ClassLoader getDefaultClassLoader() {
		ClassLoader cl = null;
		try {
             //首先获取当前线程类加载器
			cl = Thread.currentThread().getContextClassLoader();
		}
		catch (Throwable ex) {
			// Cannot access thread context ClassLoader - falling back...
		}
		if (cl == null) {
			// No thread context class loader -> use class loader of this class.
            //没有当前线程上下文,则使用当前类的类加载器
			cl = ClassUtils.class.getClassLoader();
			if (cl == null) {
				// getClassLoader() returning null indicates the bootstrap ClassLoader
                //否则返回系统的类加载器。
				try {
					cl = ClassLoader.getSystemClassLoader();
				}
				catch (Throwable ex) {
					// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
				}
			}
		}
		return cl;
    }
}

从上可以看出,ResourceLoader获取类加载器,首先获取当前线程类加载器,如果没有当前线程上下文,则使用当前类的类加载器,如果当前类没有类加载器,则获取系统的类加载器。

Resource

具体源码参见:Resource

package org.springframework.core.io;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;

/**
 *Resource接口表示一个资源描述符,即底层资源实例类型的抽象。比如文件,类路径资源。
 *如果资源以物理形式存在,输入流可以被每个资源打开,但是一个URL或文件句柄,必须调整为确定的资源。
 *实际的行为依赖于具体的实现。
 * @author Juergen Hoeller
 * @since 28.12.2003
 * @see #getInputStream()
 * @see #getURL()
 * @see #getURI()
 * @see #getFile()
 * @see WritableResource
 * @see ContextResource
 * @see UrlResource
 * @see ClassPathResource
 * @see FileSystemResource
 * @see PathResource
 * @see ByteArrayResource
 * @see InputStreamResource
 */
public interface Resource extends InputStreamSource {

	/**
	 * 判断资源实际上是否以物理形式存在。
	 */
	boolean exists();

	/**
	 * 判断资源的内容是否可以通过#getInputStream方法访问
	 * 对于特殊的资源描述,将会返回true,需要注意的是尝试读取实际的内容有可能会失败。
	 * 然而返回false,表示资源内容不可读。
	 * @see #getInputStream()
	 */
	boolean isReadable();

	/**
	 * 判断一个资源是否是一个打开的流handle。如果返回true,输入流不能读取多次,
	 * 同时在读取后,要关闭资源,避免内存泄漏。对于特殊的资源描述,将会返回false。
	 */
	boolean isOpen();

	/**
	 *返回资源的URL,如果资源不能够转化为URL,或资源不能够作为描述符访问,则抛出IO异常。
	 */
	URL getURL() throws IOException;

	/**
	 * 返回资源的URI,如果资源不能够转化为URI,或资源不能够作为描述符访问,则抛出IO异常。
	 * @since 2.5
	 */
	URI getURI() throws IOException;

	/**
	 * @see #getInputStream()
	 * 返回资源关联的文件句柄,如果文件不能够解决为一个绝对的文件路径,或在文件系统中,资源不可利用,
	 * 则抛出FileNotFoundException异常,对于一般的读或解决路径失败,则抛出IOException异常
	 */
	File getFile() throws IOException;

	/**
	 * Determine the content length for this resource.
	 * @throws IOException if the resource cannot be resolved
	 * (in the file system or as some other known physical resource type)
	 * 获取资源内容的长度,如果资源在文件系统中或其他物理资源类型,不能够解决,则抛出IOException异常
	 */
	long contentLength() throws IOException;

	/**
	 * Determine the last-modified timestamp for this resource.
	 * @throws IOException if the resource cannot be resolved
	 * (in the file system or as some other known physical resource type)
	 * 返回资源上次修改的时间戳,如果资源在文件系统中或其他物理资源类型,不能够解决,则抛出IOException异常
	 */
	long lastModified() throws IOException;

	/**
	 * Create a resource relative to this resource.
	 * 创建资源的相对路径资源。
	 * @param relativePath the relative path (relative to this resource)
	 * @return the resource handle for the relative resource
	 * @throws IOException if the relative resource cannot be determined
	 */
	Resource createRelative(String relativePath) throws IOException;

	/**
	 * Determine a filename for this resource, i.e. typically the last
	 * part of the path: for example, "myfile.txt".
	 * <p>Returns {@code null} if this type of resource does not
	 * have a filename.
	 * 获取资源的文件名
	 */
	String getFilename();

	/**
	 * Return a description for this resource,
	 * to be used for error output when working with the resource.
	 * <p>Implementations are also encouraged to return this value
	 * from their {@code toString} method.
	 * 返回资源的描述符
	 * @see Object#toString()
	 */
	String getDescription();

}

从上面可以看出,Resource实际为一个输入流资源 InputStreamSource 接口,主要提供了获取资源URL,URI,对应的文件,文件名,上次修改时间戳,文件描述符操作,以及判断资源是否存在,是否可读,是否打开等操作。需要注意的是在读取资源后,要关闭资源,以防内存泄漏。

再来看一些Resource的父接口InputStreamSource。

InputStreamSource

具体源码参见:InputStreamSource

package org.springframework.core.io;

import java.io.IOException;
import java.io.InputStream;

/**
 *InputStreamSource是一个JDK的InputStream的源对象接口。
 *此接口是资源拓展接口的基础接口。
 * 对于一种用途的流,InputStreamResource可以用于任何给定的输入流InputStream。Spring的
 * ByteArrayResource或其他基于文件的资源的实现都是一个具体的实例,允许多次读取底层流内容。
 * 这个接口对于抽象流内容非常有用,比如mail的附加物。
 *
 * @author Juergen Hoeller
 * @since 20.01.2004
 * @see java.io.InputStream
 * @see Resource
 * @see InputStreamResource
 * @see ByteArrayResource
 */
public interface InputStreamSource {

	/**
	 * 返回底层资源内容的输入流。期望每次调用创建一个fresh流。当考虑到API,比如JavaMail的时候,
	 * 输入流特别的重要,当创建mail的attachments时,需要多次读取流。在这种情况下,需要每次
	 * 调用返回一个fresh流。
	 * @return the input stream for the underlying resource (must not be {@code null})
	 * @throws java.io.FileNotFoundException if the underlying resource doesn't exist
	 * @throws IOException if the content stream could not be opened
	 */
	InputStream getInputStream() throws IOException;

}

从上可以看出,InputStreamSource主要提供了获取底层物理资源对应的输入流操作。

再来看MessageSource接口

MessageSource

具体源码参见:MessageSource

package org.springframework.context;

import java.util.Locale;

/**
 *MessageSource接口用于解决消息,支持参数化和国际化消息。
 *spring提供了两种开箱即用的实现,基于标准java.util.ResourceBundle的实现ResourceBundleMessageSource
 *和在虚拟机没有重启的情况下可以重新加载消息定义的ReloadableResourceBundleMessageSource。
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see org.springframework.context.support.ResourceBundleMessageSource
 * @see org.springframework.context.support.ReloadableResourceBundleMessageSource
 */
public interface MessageSource {

	/**
	 * 尝试解决消息,如果没有消息发现,则返回默认的消息
	 * @param code
	 * 需要寻找的消息代码,比如'calculator.noRateSet'。使用此类,鼓励使用相关类型全限定的类型名
	 * 作为base的name,这样可以避免冲突,确保最大的清晰。
	 * @param args
	 * 参数值,用于填充消息中的占位符,比如 "{0}", "{1,date}", "{2,time}",没有则为null。
	 * @param defaultMessage
	 * 如果寻找失败,则返回默认的消息
	 * @param locale the locale in which to do the lookup
	 * 本地化参数
	 * @return the resolved message if the lookup was successful;
	 * otherwise the default message passed as a parameter
	 * @see java.text.MessageFormat
	 */
	String getMessage(String code, Object[] args, String defaultMessage, Locale locale);

	/**
	 * 与上面方法不同的是,当消息不存在时,抛出NoSuchMessageException异常
	 * @see java.text.MessageFormat
	 */
	String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;

	/**
	 * 尝试解决MessageSourceResolvable中消息及消息中的参数。
	 * @throws NoSuchMessageException if the message wasn't found
	 * @see java.text.MessageFormat
	 */
	String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;

}

从上面可以看出,MessageSource接口提供了获取指定 Locale 的消息操作,消息支持占位符和国际化。同时提供了解决 MessageSourceResolvable 中的消息。

再来看MessageSourceResolvable接口的定义。

MessageSourceResolvable

具体源码参见:MessageSourceResolvable

package org.springframework.context;

/**
 *MessageSourceResolvable接口在MessageSource接口中用于消息解决。
 *Spring的字节错误验证validation类实现了此接口
 * @author Juergen Hoeller
 * @see MessageSource#getMessage(MessageSourceResolvable, java.util.Locale)
 * @see org.springframework.validation.ObjectError
 * @see org.springframework.validation.FieldError
 */
public interface MessageSourceResolvable {

	/**
	 * 返回消息关联的code
	 * @return a String array of codes which are associated with this message
	 */
	String[] getCodes();

	/**
	 * 返回消息中所有的参数
	 * @return an array of objects to be used as parameters to replace
	 * placeholders within the message text
	 * @see java.text.MessageFormat
	 */
	Object[] getArguments();

	/**
	 * 返回默认的消息,如果没有则为null
	 * @return the default message, or {@code null} if no default
	 */
	String getDefaultMessage();

}

从上面可以看出,MessageSourceResolvable主要用于MessageSource接口的消息解决,提供了获取消息code,参数及默认消息操作。

ApplicationContext接口定义

具体源码参见:ApplicationContext

package org.springframework.context;

import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.support.ResourcePatternResolver;

/**
 * Central interface to provide configuration for an application.
 * This is read-only while the application is running, but may be
 * reloaded if the implementation supports this.
 *应用上下文ApplicationContext接口是应用的配置中心接口。当应用已经运行时,应用上下文是只读的,
 *但是,如果具体的应用上下文实现支持的话,也许可以重新加载。
 * <p>An ApplicationContext provides:
 * <ul>应用上下文提供如下:
 * <li>Bean factory methods for accessing application components.
 * Inherited from {@link org.springframework.beans.factory.ListableBeanFactory}.
 * bean工厂方法访问应用的组件,从org.springframework.beans.factory.ListableBeanFactory继承
 * <li>The ability to load file resources in a generic fashion.
 * Inherited from the {@link org.springframework.core.io.ResourceLoader} interface.
 * 加载一般文件资源的能力,从org.springframework.core.io.ResourceLoader继承
 * <li>The ability to publish events to registered listeners.
 * Inherited from the {@link ApplicationEventPublisher} interface.
 * 发布时间到监听器的功能,从 ApplicationEventPublisher继承。
 * <li>The ability to resolve messages, supporting internationalization.
 * Inherited from the {@link MessageSource} interface.
 * 解决消息,支持国际化的功能,从MessageSource继承。
 * <li>Inheritance from a parent context. Definitions in a descendant context
 * will always take priority. This means, for example, that a single parent
 * context can be used by an entire web application, while each servlet has
 * its own child context that is independent of that of any other servlet.
 * 父上下文的继承性(HierarchicalBeanFactory)。定义在子孙上下文中的bean定义将会有限考虑。这意味着,一个单独的父上下文可以被整个web应用上下文所使用,
 * 然而每个servlet有自己额上下文,独立于其他servlet。这一点体现在,当我们使用spring的核心容器特性和spring mvc时,在web.xml中,
 * 我们有两个配置一个是上下文监听器(org.springframework.web.context.ContextLoaderListener),
 * 同时需要配置应用上下文bean的定义配置,一般是ApplicationContext.xml,另一个是Servlet分发器(org.springframework.web.servlet.DispatcherServlet),
 * 同时需要配置WebMVC相关配置,一般是springmvc.xml。应用一般运行的在Web容器中,Web容器可以访问应用上下文,同时Web容器的Servlet也可以访问应用上下文。
 * </ul>
 *
 * <p>In addition to standard {@link org.springframework.beans.factory.BeanFactory}
 * lifecycle capabilities, ApplicationContext implementations detect and invoke
 * {@link ApplicationContextAware} beans as well as {@link ResourceLoaderAware},
 * {@link ApplicationEventPublisherAware} and {@link MessageSourceAware} beans.
 * 除了标准额org.springframework.beans.factory.BeanFactory的声明周期功能之外,应用上下文的实现可以
 * 探测和调用ApplicationContextAwarebean,ResourceLoaderAware,ApplicationEventPublisherAware,MessageSourceAware。
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see ConfigurableApplicationContext
 * @see org.springframework.beans.factory.BeanFactory
 * @see org.springframework.core.io.ResourceLoader
 */
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

	/**
	 * Return the unique id of this application context.
	 * 返回应用上下文的id,没有则为null
	 * @return the unique id of the context, or {@code null} if none
	 */
	String getId();

	/**
	 * Return a name for the deployed application that this context belongs to.
	 * 返回应用上下文所属的部署应用名称,默认为空字符串
	 * @return a name for the deployed application, or the empty String by default
	 */
	String getApplicationName();

	/**
	 * Return a friendly name for this context.
	 * 返回上下文友好的展示name
	 * @return a display name for this context (never {@code null})
	 */
	String getDisplayName();

	/**
	 * Return the timestamp when this context was first loaded.
	 * 返回上下文第一次加载的时间戳
	 * @return the timestamp (ms) when this context was first loaded
	 */
	long getStartupDate();

	/**
	 * Return the parent context, or {@code null} if there is no parent
	 * and this is the root of the context hierarchy.
	 * 返回父上下文,如果没有父上下文,或这个是上下文的跟,则返回null。
	 * @return the parent context, or {@code null} if there is no parent
	 */
	ApplicationContext getParent();

	/**
	 * Expose AutowireCapableBeanFactory functionality for this context.
	 * 暴露上下文的AutowireCapableBeanFactory功能性
	 * <p>This is not typically used by application code, except for the purpose of
	 * initializing bean instances that live outside of the application context,
	 * applying the Spring bean lifecycle (fully or partly) to them.
	 * 应用编码中不建议使用此AutowireCapableBeanFactory,自动装配bean工厂,用于初始化生存在应用上下文外部的实例,
	 * 并完全或部分控制Spring的bean的声明周期。
	 * <p>Alternatively, the internal BeanFactory exposed by the
	 * {@link ConfigurableApplicationContext} interface offers access to the
	 * {@link AutowireCapableBeanFactory} interface too. The present method mainly
	 * serves as a convenient, specific facility on the ApplicationContext interface.
	 * 另外,内部的bean工厂通过ConfigurableApplicationContext接口提供了访问AutowireCapableBeanFactory的操作。
	 * 此方法主要是为应用上下文提供方便。
	 * <p><b>NOTE: As of 4.2, this method will consistently throw IllegalStateException
	 * after the application context has been closed.</b> In current Spring Framework
	 * versions, only refreshable application contexts behave that way; as of 4.2,
	 * all application context implementations will be required to comply.
	 * 需要注意的是,在spring4.2版本中,当应用上下文关闭的时候,此方法将会抛出IllegalStateException。
	 * 在当前spring4.3.x框架的版本中,仅仅可刷新应用上下行为相同;在spring4.2中,所有的应用上下文的实现都行遵守
	 * 此规则。
	 * @return the AutowireCapableBeanFactory for this context
	 * 返回上下文的AutowireCapableBeanFactory
	 * @throws IllegalStateException if the context does not support the
	 * {@link AutowireCapableBeanFactory} interface, or does not hold an
	 * autowire-capable bean factory yet (e.g. if {@code refresh()} has
	 * never been called), or if the context has been closed already
	 * 如果上下文不支持AutowireCapableBeanFactory接口,或者没有持有一个可刷新的AutowireCapableBeanFactory实例,
	 * 即{@code refresh()还没有被调用的,或者上下文件已经关闭,则抛出IllegalStateException。
	 * @see ConfigurableApplicationContext#refresh()
	 * @see ConfigurableApplicationContext#getBeanFactory()
	 */
	AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

从上,我们可以看出,ApplicationContext接口主要提供了获取父上下文,自动装配bean工厂 AutowireCapableBeanFactory,应用上下文name,展示name,启动时间戳及应用id的操作。应用上下文继承了 EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver ,具有了访问bean容器中组件, 配置环境,加载文件或类路径资源,发布应用事件到监听器,已经解决国际化消息的功能。另外需要注意的是,应用上下文具有,父上下文的继承性(HierarchicalBeanFactory)。定义在子孙上下文中的bean定义将会有限考虑。这意味着,一个单独的父上下文可以被整个web应用上下文所使用。这一点体现在,当我们使用spring的核心容器特性和spring mvc时,在web.xml中,我们有两个配置一个是上下文监听器(org.springframework.web.context.ContextLoaderListener),同时需要配置应用上下文bean的定义配置,一般是ApplicationContext.xml,另一个是Servlet分发器(org.springframework.web.servlet.DispatcherServlet), 同时需要配置WebMVC相关配置,一般是springmvc.xml。应用一般运行的在Web容器中,Web容器可以访问应用上下文,同时Web容器的Servlet也可以访问应用上下文,然而每个servlet有自己的上下文,独立于其他servlet。

最后以应用上下文接口的类图来结束本篇文章:

ApplicationContext

关于 AutowireCapableBeanFactory 我们将在下一篇文章中,再讲。

总结

BeanFactory接口主要是主要提供了根据名称或类型获取bean的相关方法,以及判断bean是否匹配指定类型或判断是否包含指定name的共享单实例或多实例bean。需要注意的是,如果bean工厂的实现是可继承工厂,那么调用这些方法,如果没有在当前bean工厂实例中找到,将会从父工厂中查到。另外还需要注意一点,判断一个bean是否为共享单例模式,可以使用isSingleton方法,返回true,即是,返回false,并不能表示bean是多实例bean,具体要用isPrototype方法判断,同理isPrototype方法也是如此。

ListableBeanFactory接口主要提供了判断是否存在给定name的bean定义,获取bean定义数量,获取指定类型的bean定义的name集或name与bean实例的映射集,获取待指定注解的bean定义的name或name与bean实例的映射集,以及获取给定name对应的bean的注定注解实例。需要注意的是,提供的操作不会到可继承bean工厂中去搜索,但包括BeanFactoryUtils工具类获取bean工厂的祖先bean工厂。另外getBeanNamesForType和getBeanNamesForAnnotation方法可以通过includeNonSingletons和allowEagerInit, 控制搜索bean的作用域范围和是否初始化懒加载单例模式bean与工厂bean。

HierarchicalBeanFactory接口标志一个以可继承bean工厂,我们可以通过 ConfigurableBeanFactory 接口的 setParentBeanFactory 方法配置bean工厂的父类工厂,主要提供获取父工厂操作,以及判断在本地bean工厂中是否存在指定name对应的bean的操作,但忽略祖先上下文中的bean定义。

事件发布接口ApplicationEventPublisher,主要作为ApplicationContext的父接口,封装了事件发布功能,提供了事件发布功能。当事件发布时,通知所有注册到当前应用关注ApplicationEvent事件event的监听器,如果发布的事件不是 ApplicationEvent, 则将会包装成 PayloadApplicationEvent

ApplicationEvent用于表示应用发生的事件,事件包括事件发生的时间和事件发生源。

监听器接口ApplicationListener,是基于标准JDK的观察者模式的接口java.util.EventListener, 从spring3.0以后,应用监听器需要声明关注的应用事件类型。当监听器注册到spring的应用上下文ApplicationContext时, 将会根据事件类型过滤监听器,匹配事件类型的监听器才会被通知。接口主要提供了处理事件操作。

EnvironmentCapable接口,表示包括或暴露一个Environment环境引用的组件。Spring的所有应用上下文都是EnvironmentCapable接口实现,用于应用上下文与环境交互。需要注意的是,ApplicationContext扩展了EnvironmentCapable接口,通过getEnvironment方法暴露环境配置;然而 ConfigurableApplicationContext 将会重定义getEnvironment方法,返回一个ConfigurableEnvironment。 两种方法带来的效果是,在环境配置Environment对象在 ConfigurableApplicationContext 可访问以前,都是自读的,可以理解为 ConfigurableApplicationContext 的getEnvironment方法返回的环境象时可修改的。接口提供了获取环境配置操作。

Environment接口同时是一个 PropertyResolver 接口,提供了获取激活配置 Profiles 和默认配置的操作,同时提供了判断配置是否激活操作。应用环境Environment有一个或多个配置 Profiles,配置可以理解为配置集或类型,比如开发,测试,体验,生产等环境。当应用存在需要引用属性的情况,我们可以从环境中获取,应为环境是一个 PropertyResolver。环境配置对象必须通过ConfigurableEnvironment接口进行配置,所有AbstractApplicationContext的子类,都可通过getEnvironment方法返回一个可配置环境接口ConfigurableEnvironment。

PropertyResolver才出现,PropertyResolver注意根据属性源,是否包含给定属性,获取相关属性的值及获取给定类型属性的值操作,同时提供了替换给定上文本中的引用属性“${…}”操作。

ResourcePatternResolver拓展了 ResourceLoader 接口,主要用于解决或加载给定路径下的资源文件,ResourcePatternResolver建议使用以 “classpath*:”为前缀,创建一个匹配class路径的所有资源。

ResourceLoader接口用于加载资源class路径或文件系统等类型资源,提供获取给定位置的资源操作和获取系统ClassLoader。ResourceLoader获取类加载器,首先获取当前线程类加载器,如果没有当前线程上下文,则使用当前类的类加载器,如果当前类没有类加载器,则获取系统的类加载器。

Resource实际为一个输入流资源 InputStreamSource 接口,主要提供了获取资源URL,URI,对应的文件,文件名,上次修改时间戳,文件描述符操作,以及判断资源是否存在,是否可读,是否打开等操作。需要注意的是在读取资源后,要关闭资源,以防内存泄漏。

InputStreamSource主要提供了获取底层物理资源对应的输入流操作。

MessageSource接口提供了获取指定 Locale 的消息操作,消息支持占位符和国际化。同时提供了解决 MessageSourceResolvable 中的消息。

MessageSourceResolvable主要用于MessageSource接口的消息解决,提供了获取消息code,参数及默认消息操作。

ApplicationContext接口主要提供了获取父上下文,自动装配bean工厂 AutowireCapableBeanFactory,应用上下文name,展示name,启动时间戳及应用id的操作。应用上下文继承了 EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver ,具有了访问bean容器中组件, 配置环境,加载文件或类路径资源,发布应用事件到监听器,已经解决国际化消息的功能。另外需要注意的是,应用上下文具有,父上下文的继承性(HierarchicalBeanFactory)。定义在子孙上下文中的bean定义将会有限考虑。这意味着,一个单独的父上下文可以被整个web应用上下文所使用。这一点体现在,当我们使用spring的核心容器特性和spring mvc时,在web.xml中,我们有两个配置一个是上下文监听器(org.springframework.web.context.ContextLoaderListener),同时需要配置应用上下文bean的定义配置,一般是ApplicationContext.xml,另一个是Servlet分发器(org.springframework.web.servlet.DispatcherServlet), 同时需要配置WebMVC相关配置,一般是springmvc.xml。应用一般运行的在Web容器中,Web容器可以访问应用上下文,同时Web容器的Servlet也可以访问应用上下文,然而每个servlet有自己的上下文,独立于其他servlet。

ResourceUtils

public abstract class ResourceUtils {

	/** Pseudo URL prefix for loading from the class path: "classpath:" */
	public static final String CLASSPATH_URL_PREFIX = "classpath:";

	/** URL prefix for loading from the file system: "file:" */
	public static final String FILE_URL_PREFIX = "file:";

	/** URL prefix for loading from a jar file: "jar:" */
	public static final String JAR_URL_PREFIX = "jar:";

	/** URL prefix for loading from a war file on Tomcat: "war:" */
	public static final String WAR_URL_PREFIX = "war:";

	/** URL protocol for a file in the file system: "file" */
	public static final String URL_PROTOCOL_FILE = "file";

	/** URL protocol for an entry from a jar file: "jar" */
	public static final String URL_PROTOCOL_JAR = "jar";

	/** URL protocol for an entry from a war file: "war" */
	public static final String URL_PROTOCOL_WAR = "war";

	/** URL protocol for an entry from a zip file: "zip" */
	public static final String URL_PROTOCOL_ZIP = "zip";

	/** URL protocol for an entry from a WebSphere jar file: "wsjar" */
	public static final String URL_PROTOCOL_WSJAR = "wsjar";

	/** URL protocol for an entry from a JBoss jar file: "vfszip" */
	public static final String URL_PROTOCOL_VFSZIP = "vfszip";

	/** URL protocol for a JBoss file system resource: "vfsfile" */
	public static final String URL_PROTOCOL_VFSFILE = "vfsfile";

	/** URL protocol for a general JBoss VFS resource: "vfs" */
	public static final String URL_PROTOCOL_VFS = "vfs";

	/** File extension for a regular jar file: ".jar" */
	public static final String JAR_FILE_EXTENSION = ".jar";

	/** Separator between JAR URL and file path within the JAR: "!/" */
	public static final String JAR_URL_SEPARATOR = "!/";

	/** Special separator between WAR URL and jar part on Tomcat */
	public static final String WAR_URL_SEPARATOR = "*/";
}