1. 整体认识
先贴一张整体结构图:
-
在之前的文章解析了ApplicationContext和ConfigurableApplicationContext,现在再来了解这个类就清晰很多
-
AbstractApplicationContext是ConfigurableApplicationContext接口的抽象实现,同事继承了默认资源解析类。其实在ConfigurableApplicationContext中有增加协议解析器的拓展函数,通过这个类集成DefaultResourceLoader类就能解释得通了,ConfigurableApplicationContext中的是拓展点,如果默认资源加载不能满足需求,在通过从写ConfigurableApplicationContext中的增加协议解析器来实现拓展。
从整体结构可以知道,这个类是整个ApplicationContext类的重点,基本的实现都集中在这个类中。这也是我们了解高级容器必经之路。继续撸源码
2. 具体源码分析
2.1 配置
一上来就搞了一下和ConfigurableApplicationContext相似的操作,各特定bean设置专有名称
将messageSource、lifecycleProcessor、applicationEventMulticaster这三个名称的bean起个大写的名字。
没想想通的是,为什么不在ConfigurableApplicationContext全部配置得了,非得分开呢?
/**
* Name of the MessageSource bean in the factory.
* If none is supplied, message resolution is delegated to the parent.
* @see MessageSource
*/
public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
/**
* Name of the LifecycleProcessor bean in the factory.
* If none is supplied, a DefaultLifecycleProcessor is used.
* @see org.springframework.context.LifecycleProcessor
* @see org.springframework.context.support.DefaultLifecycleProcessor
*/
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
/**
* Name of the ApplicationEventMulticaster bean in the factory.
* If none is supplied, a default SimpleApplicationEventMulticaster is used.
* @see org.springframework.context.event.ApplicationEventMulticaster
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
*/
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
接下来这两个配置,
/**
* Boolean flag controlled by a {@code spring.spel.ignore} system property that instructs Spring to
* ignore SpEL, i.e. to not initialize the SpEL infrastructure.
* ~~~~~~~~~~~~~~~~~~~~~~
* 由spring.spel.ignore系统属性控制的布尔标志,该属性指示Spring忽略SpEL,
* 即不初始化SpEL基础结构。默认值为“ false”
* ~~~~~~~~~~~~~~~~~~~~~~~
* <p>The default is "false".
*/
private static final boolean shouldIgnoreSpel = SpringProperties.getFlag("spring.spel.ignore");
/**
* Whether this environment lives within a native image.
* Exposed as a private static field rather than in a {@code NativeImageDetector.inNativeImage()} static method due to https://github.com/oracle/graal/issues/2594.
* @see <a href="https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java">ImageInfo.java</a>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~
* 此环境是否存在于本地映像中
* ~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null);
2.2 很重要的一个配置
static {
// Eagerly load the ContextClosedEvent class to avoid weird classloader issues
// on application shutdown in WebLogic 8.1. (Reported by Dustin Woods.)
ContextClosedEvent.class.getName();
}
其实这里我也不是很清楚,这需要我继续撸源码,看后面的理解。后面再来解答
2.3 属性定义
static {
// Eagerly load the ContextClosedEvent class to avoid weird classloader issues
// on application shutdown in WebLogic 8.1. (Reported by Dustin Woods.)
ContextClosedEvent.class.getName();
}
/** Logger used by this class. Available to subclasses. */
// 获取日志类
protected final Log logger = LogFactory.getLog(getClass());
/** Unique id for this context, if any. */
// 该容器唯一标识
private String id = ObjectUtils.identityToString(this);
/** Display name. */
// 该容器显示名称,和id一样?
private String displayName = ObjectUtils.identityToString(this);
/** Parent context. */
// 父容器
@Nullable
private ApplicationContext parent;
/** Environment used by this context. */
// 该容器的环境,这里的环配置类。关于环境配置,Spring有自己的一套,下面有写介绍。
@Nullable
private ConfigurableEnvironment environment;
/** BeanFactoryPostProcessors to apply on refresh. */
// 在容器启动后的后置处理,这里用的一个集合来保存,证明后置处理有很多,
private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new ArrayList<>();
/** System time in milliseconds when this context started. */
// 容器启动的时间,毫秒
private long startupDate;
/** Flag that indicates whether this context is currently active. */
// 推测当前该容器是否启动的标志,也就是提供判断该容器当前事故处于启动状态
private final AtomicBoolean active = new AtomicBoolean();
/** Flag that indicates whether this context has been closed already. */
// 推测当前该容器是否关闭的标志
private final AtomicBoolean closed = new AtomicBoolean();
/** Synchronization monitor for the "refresh" and "destroy". */
// 同步监视器,用于“刷新”和“销毁”。
private final Object startupShutdownMonitor = new Object();
/** Reference to the JVM shutdown hook, if registered. */
// 如果已将注册了,参考JVM关闭钩子,
@Nullable
private Thread shutdownHook;
/** ResourcePatternResolver used by this context. */
// 此容器使用的资源模式加载器
private ResourcePatternResolver resourcePatternResolver;
/** LifecycleProcessor for managing the lifecycle of beans within this context. */
// 管理该容器中bean的生命周期
@Nullable
private LifecycleProcessor lifecycleProcessor;
/** MessageSource we delegate our implementation of this interface to. */
// 消息的实现类
@Nullable
private MessageSource messageSource;
/** Helper class used in event publishing. */
// 事件发布的帮助类
@Nullable
private ApplicationEventMulticaster applicationEventMulticaster;
/** Application startup metrics. **/
// 应用程序启动指标,这里就是应用程序的启动点
private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
/** Statically specified listeners. */
// 静态指定的侦听器。
private final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
/** Local listeners registered before refresh. */
// 刷新之前已注册本地侦听器,关于监听器的作用,有待探究。先知道,后面整体理解
@Nullable
private Set<ApplicationListener<?>> earlyApplicationListeners;
/** ApplicationEvents published before the multicaster setup. */
// 在多播器设置之前发布的ApplicationEvents
@Nullable
private Set<ApplicationEvent> earlyApplicationEvents;
/**
* Create a new AbstractApplicationContext with no parent.
* 创建一个没有父级的新AbstractApplicationContext,这里指定了资源加载器
*/
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
/**
* Create a new AbstractApplicationContext with the given parent context.
* 使用给定的父上下文创建一个新的AbstractApplicationContext。
* @param parent the parent context
*/
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this();
setParent(parent);
}
2.3.1 ConfigurableEnvironment 介绍
上图就是环境变量结构,Spring提供了一套环境配置标准,大多数需要配置环境的都将实现该接口。至于该接口的到底规定哪些东西。
看到这个结构是不是有种似曾相识的感觉,你看ioc的整体设计,红框内的。这就是Spring的整体架构思想。
- 定义基础接口规范
- 定义很多拓展接口实现基础接口
- 定义一个接口来集合所有的拓展接口
- 定义一个配置接口
- 定义一个抽象实现类,这个抽象实现了就是开发用的基础了,要实现继续拓展,然后继承该类就行
在阅读源码时,完全可以照着这个顺序去解析,有助于把我整体性。
Spring的设计是有这个规律的,整体架构的规律是这样。提供拓展点,在哪个层次都可以拓展,提供高定制性。如果不满足Spring提供的,我们可以继承最初的接口规范,一层一层的进行拓展。如果想要在他的设计上完善,更具需求的在不同的层次接入拓展。在以后的设计解决方案是,可以参考。我看的源码整体架构不是很多,比较不出优劣,等以后积累一定程度在说,但是Spring的思想是可以借鉴的。
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {
/**
* Specify the set of profiles active for this {@code Environment}. Profiles are
* evaluated during container bootstrap to determine whether bean definitions
* should be registered with the container.
* <p>Any existing active profiles will be replaced with the given arguments; call
* with zero arguments to clear the current set of active profiles. Use
* {@link #addActiveProfile} to add a profile while preserving the existing set.
* ~~~~~~~~~~~~~~~~~~~~~~~
* 中文解释
* 指定为此Environment激活的配置文件集。
* ~~~~~~~~~~~~~~~~~~~~~~~
* @throws IllegalArgumentException if any profile is null, empty or whitespace-only
* @see #addActiveProfile
* @see #setDefaultProfiles
* @see org.springframework.context.annotation.Profile
* @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
*/
void setActiveProfiles(String... profiles);
/**
* Add a profile to the current set of active profiles.
* ~~~~~~~~~~~~~~~~
* 中文解释:
* 在当前激活文件集中增加配置文件
* ~~~~~~~~~~~~~~~~
* @throws IllegalArgumentException if the profile is null, empty or whitespace-only
* @see #setActiveProfiles
*/
void addActiveProfile(String profile);
/**
* Specify the set of profiles to be made active by default if no other profiles
* ~~~~~~~~~~~~~~
* 中文解释:
* 如果没有通过setActiveProfiles显式激活其他配置文件,则指定默认情况下将其激活的配置文件集
* ~~~~~~~~~~~~~~
* are explicitly made active through {@link #setActiveProfiles}.
* @throws IllegalArgumentException if any profile is null, empty or whitespace-only
* @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
*/
void setDefaultProfiles(String... profiles);
/**
* Return the {@link PropertySources} for this {@code Environment} in mutable form,
* allowing for manipulation of the set of {@link PropertySource} objects that should
* be searched when resolving properties against this {@code Environment} object.
* The various {@link MutablePropertySources} methods such as
* ~~~~~~~~~~~~~~~~~~~
* 以可变形式返回此Environment的PropertySources ,
* 从而允许处理在针对该Environment对象解析属性时应搜索的PropertySource对象集。
* 各种MutablePropertySources方法
* (例如addFirst , addLast , addBefore和addAfter允许对属性源顺序进行细粒度控制。
* 例如,这在确保某些用户定义的属性源具有优先于默认属性源
* (例如系统属性集或系统环境变量集)的搜索优先级时很有用
* ~~~~~~~~~~~~~~~~~~~
* 其实我是没有理解这个是什么作用,意思就是说返回一个可变的环境属性,提供各种方法来操作配置属性优先级。
* 这里姑且算是,设置谁先执行,谁先生效
* ~~~~~~~~~~~~~~~~~~~
* {@link MutablePropertySources#addFirst addFirst},
* {@link MutablePropertySources#addLast addLast},
* {@link MutablePropertySources#addBefore addBefore} and
* {@link MutablePropertySources#addAfter addAfter} allow for fine-grained control
* over property source ordering. This is useful, for example, in ensuring that
* certain user-defined property sources have search precedence over default property
* sources such as the set of system properties or the set of system environment
* variables.
* @see AbstractEnvironment#customizePropertySources
*/
MutablePropertySources getPropertySources();
/**
* Return the value of {@link System#getProperties()} if allowed by the current
* {@link SecurityManager}, otherwise return a map implementation that will attempt
* to access individual keys using calls to {@link System#getProperty(String)}.
* <p>Note that most {@code Environment} implementations will include this system
* properties map as a default {@link PropertySource} to be searched. Therefore, it is
* recommended that this method not be used directly unless bypassing other property
* sources is expressly intended.
* <p>Calls to {@link Map#get(Object)} on the Map returned will never throw
* ~~~~~~~~~~~~~~~~~~~~~~
* 中文解释:
* 如果当前SecurityManager允许,返回System.getProperties()的值,
* 否则返回一个映射实现,该实现将尝试使用对System.getProperty(String)调用来访问各个键
* 请注意,大多数Environment实施都将包含此系统属性映射,作为要搜索的默认PropertySource 。
* 因此,建议不要直接使用此方法,除非明确打算绕过其他属性源。
* 在返回的Map上调用Map.get(Object)永远不会抛出IllegalAccessException ;
* 如果SecurityManager禁止访问属性,则将返回null并发出INFO级别的日志消息,指出该异常
* ~~~~~~~~~~~~~~~~~~~~~~
* 通过getSecurityManager()获取SecurityManager来判断是否被允许获取系统属性
* ~~~~~~~~~~~~~~~~~~~~~~
* {@link IllegalAccessException}; in cases where the SecurityManager forbids access
* to a property, {@code null} will be returned and an INFO-level log message will be
* issued noting the exception.
*/
Map<String, Object> getSystemProperties();
/**
* Return the value of {@link System#getenv()} if allowed by the current
* {@link SecurityManager}, otherwise return a map implementation that will attempt
* to access individual keys using calls to {@link System#getenv(String)}.
* <p>Note that most {@link Environment} implementations will include this system
* environment map as a default {@link PropertySource} to be searched. Therefore, it
* is recommended that this method not be used directly unless bypassing other
* property sources is expressly intended.
* <p>Calls to {@link Map#get(Object)} on the Map returned will never throw
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~
* 如果当前SecurityManager允许,返回System.getenv()的值,
* 否则返回一个映射实现,该实现将尝试使用对System.getenv(String)调用来访问各个键。
* 请注意,大多数Environment实现都将包含此系统环境映射作为要搜索的默认PropertySource 。
* 因此,建议不要直接使用此方法,除非明确打算绕过其他属性源。
* 在返回的Map上调用Map.get(Object)永远不会抛出IllegalAccessException ;
* 如果SecurityManager禁止访问属性,则将返回null并发出INFO级别的日志消息,指出该异常
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~
* {@link IllegalAccessException}; in cases where the SecurityManager forbids access
* to a property, {@code null} will be returned and an INFO-level log message will be
* issued noting the exception.
*/
Map<String, Object> getSystemEnvironment();
/**
* Append the given parent environment's active profiles, default profiles and
* property sources to this (child) environment's respective collections of each.
* <p>For any identically-named {@code PropertySource} instance existing in both
* parent and child, the child instance is to be preserved and the parent instance
* discarded. This has the effect of allowing overriding of property sources by the
* child as well as avoiding redundant searches through common property source types,
* e.g. system environment and system properties.
* <p>Active and default profile names are also filtered for duplicates, to avoid
* confusion and redundant storage.
* <p>The parent environment remains unmodified in any case. Note that any changes to
* the parent environment occurring after the call to {@code merge} will not be
* reflected in the child. Therefore, care should be taken to configure parent
* property sources and profile information prior to calling {@code merge}.
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* 将给定的父环境的活动配置文件,默认配置文件和属性源追加到此(子)环境各自的集合中。
* 对于父代和子代中都存在的任何名称相同的PropertySource实例,将保留子代实例,并丢弃父代实例。
* 这样的效果是允许子级覆盖属性源,并避免通过常见属性源类型(例如系统环境和系统属性)进行冗余搜索。
* 活动和默认配置文件名称也会被过滤,以防重复,以避免混淆和冗余存储。
* 在任何情况下,父环境都保持不变。
* 请注意,在merge调用之后对父环境所做的任何更改都不会反映在子项中。
* 因此,在调用merge之前,应谨慎配置父属性源和配置文件信息
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* 默认情况下父环境的配置文件遗传到子环境配置文件,单是出现两者同名的配置文件,
* 子类还是用自己的配置文件,而不是用父环境的配置文件,就近原则。
* 在调用这个方法merge()之前父配置文件的更改是会同步到子环境中,但是这个方法的地调用,
* 这种特性就消失了,子环境自己玩,不受到付环境的影响。
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* @param parent the environment to merge with
* @since 3.1.2
* @see org.springframework.context.support.AbstractApplicationContext#setParent
*/
void merge(ConfigurableEnvironment parent);
上面的方法中文翻译我借助的谷歌翻译,在加上自己的理解。具体的用处,这种辅助的,需要在应用中慢慢理解,IOC的设计中一定会用到,到时候在提及。
2.4 实现ApplicationContext接口
下面这些方法就没必要介绍了,如方法名可知道。之前对ApplicationContext的认识得知,该接口有对基础属性的get方法,下面就是这些方法的实现。
@Override
public void setId(String id) {
this.id = id;
}
@Override
public String getId() {
return this.id;
}
@Override
public String getApplicationName() {
return "";
}
/**
* Set a friendly name for this context.
* Typically done during initialization of concrete context implementations.
* <p>Default is the object id of the context instance.
*/
public