0xTTEPX

Just do it, deeply...

Follow me on GitHub

ConfigurableApplicationContext接口定义

write by donaldhan, 2017-12-20 08:32

引言

上一篇文章,我们看了AutowireCapableBeanFactory接口,主要提供的创建bean实例,自动装配bean属性,应用bean配置属性,初始化bean,应用bean后处理器 BeanPostProcessor ,解决bean依赖和销毁bean操作。对于自动装配,主要提供了根据bean的name,类型和构造自动装配方式。一般不建议在在代码中直接使用AutowireCapableBeanFactory接口,我们可以通过应用上下文的ApplicationContext#getAutowireCapableBeanFactory()方法或者通过实现BeanFactoryAware,获取暴露的bean工厂,然后转换为AutowireCapableBeanFactory。NamedBeanHolder用于表示bean的name和实例的关系句柄。NamedBeanHolder可以用于Spring的根据bean的name自动装配和AOP相关的功能,避免产生不可靠的依赖。

AutowireCapableBeanFactory

目录

看完了AutowireCapableBeanFactory和ApplicationContext接口的定义,我们接着,来看 ConfigurableApplicationContext 接口的定义,再看之前先看一下父类接口Lifecycle和Closeable的定义: 先来看Lifecycle

Lifecycle

具体源码参见:Lifecycle

package org.springframework.context;

/**
*  Lifecycle接口是一个普通的接口,定义了控制声明周期的启动和停止操作,用于异步处理的情况。
*注意此接口不意味可以自动启动,如果有这方面的需求,可以考虑实现{@link SmartLifecycle}接口。
 * 此接口可以被组件或容器实现,比如典型的spring上下中bean定义和spring应用上下文ApplicationContext。
 * 容器应该传播启动和停止信道到所有子容器中的组件。比如在运行时环境下的停止和重启情况。
 *此接口可以通过JMX直接调用或管理操作。在管理操作的情况下, {@link org.springframework.jmx.export.MBeanExporter}定义
 *为{@link org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler},限制在声明周期范围内
 *的活动组件的可视性。
 *注意:声明周期接口,仅仅支持顶层的单例bean。在其他组件中,声明周期接口将不可探测,因此将会忽略。拓展{@link SmartLifecycle}接口
 *提供的继承应用上下文的启动和关闭阶段。
 * @author Juergen Hoeller
 * @since 2.0
 * @see SmartLifecycle
 * @see ConfigurableApplicationContext
 * @see org.springframework.jms.listener.AbstractMessageListenerContainer
 * @see org.springframework.scheduling.quartz.SchedulerFactoryBean
 */
public interface Lifecycle {

	/**
	 * 启动当前组件。如果组件已将在运行,不应该抛出异常。在容器环境下,将传播启动信号到应用的所有组件。
	 * @see SmartLifecycle#isAutoStartup()
	 */
	void start();

	/**
	 * 停止当前组件,在同步环境下,在方法返回后,组件完全停止。当异步停止行为需要的时候,可以考虑实现 {@link SmartLifecycle}接口的
	 * {@code stop(Runnable)}方法。需要注意的是:不保证停止通知发生在析构之前:在正常的关闭操作下,{@code Lifecycle} bean将会
	 * 在一般的析构回调之前,将会接受一个停止通知;然而在上下文生命周期内的热刷新或刷新尝试中断,仅仅销毁方法将会调用。如果组件还没有启动,
	 * 则不应该抛出异常。在容器环境下,应该传播停止信号到所有的组件。
	 * @see SmartLifecycle#stop(Runnable)
	 * @see org.springframework.beans.factory.DisposableBean#destroy()
	 */
	void stop();

	/**
	 * 判断当前组件是否运行。在容器中,如果所有应用的组件当前都在运行,则返回true
	 * @return whether the component is currently running
	 */
	boolean isRunning();

}

从上可以看出,Lifecycle接口提供了启动和关闭操作,以及判断当前组件是否运行操作。需要注意的是启动和停止操作,将会传播给容器中的所有子容器中的组件。对于停止操作,不保证停止通知发生在析构之前。对于判断当前组件是否运行操作,如果组件是容器,只有在容器中所有组件包括子容器中的组件,都在运行的情况下,才返回true。

再来看一下Closeable接口,这个时JDK的范畴。

Closeable

package java.io;

import java.io.IOException;

/**
 * A {@code Closeable} is a source or destination of data that can be closed.
 * The close method is invoked to release resources that the object is
 * holding (such as open files).
 *Closeable是一个可以关闭的数据源或目的。close方法被调用,将释放相关的资源。
 * @since 1.5
 */
public interface Closeable extends AutoCloseable {

    /**
     * Closes this stream and releases any system resources associated
     * with it. If the stream is already closed then invoking this
     * method has no effect.
     *关闭当前流,释放相关资源。如果流已关闭,则调用方法没有任何作用
     * <p> As noted in {@link AutoCloseable#close()}, cases where the
     * close may fail require careful attention. It is strongly advised
     * to relinquish the underlying resources and to internally
     * <em>mark</em> the {@code Closeable} as closed, prior to throwing
     * the {@code IOException}.
     *
     * @throws IOException if an I/O error occurs
     */
    public void close() throws IOException;
}

ConfigurableApplicationContext除了继承了 LifecycleCloseable,还继承了 ApplicationContext,关于应用上下文接口我们在前面,已将在,这里不再说,忘掉的可以去查阅。
下面进入我们这篇文章的核心部分ConfigurableApplicationContext接口定义

ConfigurableApplicationContext接口定义

具体源码参见:ConfigurableApplicationContext

package org.springframework.context;

import java.io.Closeable;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ProtocolResolver;

/**
 *ConfigurableApplicationContext接口可以被大多数的应用上下文实现。为配置应用上下文提供便利,
 *另外,可以作为应用上下文的客户端。
 *相关配配置和生命周期方法被封装在此接口,以避免应用上下文客户端看见。接口当前方法啊,应该在启动和关闭代码间调用。
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 03.11.2003
 */
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {

	/**
	 * 配置文件路径分割符
	 * @see org.springframework.context.support.AbstractXmlApplicationContext#setConfigLocation
	 * @see org.springframework.web.context.ContextLoader#CONFIG_LOCATION_PARAM
	 * @see org.springframework.web.servlet.FrameworkServlet#setContextConfigLocation
	 */
	String CONFIG_LOCATION_DELIMITERS = ",; \t\n";

	/**
	 * bean工厂内类型转化ConversionService bean的name,没有则为默认值。
	 * @see org.springframework.core.convert.ConversionService
	 * @since 3.0
	 */
	String CONVERSION_SERVICE_BEAN_NAME = "conversionService";

	/**
	 * bean工厂中LoadTimeWeaver bean的name,如果存在LoadTimeWeaver这样的bean,为了允许LoadTimeWeaver可以处理
	 * 实际的bean类型,则上下文将使用匹配类型的临时类型加载器。
	 * @since 2.5
	 * @see org.springframework.instrument.classloading.LoadTimeWeaver
	 */
	String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";

	/**
	 * Name of the {@link Environment} bean in the factory.
	 * bean工厂中环境的name
	 * @since 3.1
	 */
	String ENVIRONMENT_BEAN_NAME = "environment";

	/**
	 * Name of the System properties bean in the factory.
	 * bean工厂中系统属性bean的name
	 * @see java.lang.System#getProperties()
	 */
	String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";

	/**
	 * Name of the System environment bean in the factory.
	 * bean工厂中系统bean的name
	 * @see java.lang.System#getenv()
	 */
	String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";


	/**
	 * Set the unique id of this application context.
	 * 设置应用上下文的唯一id
	 * @since 3.0
	 */
	void setId(String id);

	/**
	 * 设置应用上下文的父上下文。主要父上下文不可以改变:当类型的对象创建时,如果对象不可用,对象应在构造范围之外。
	 * 比如,Web上下文WebApplicationContext的配置
	 * @param parent the parent context
	 * @see org.springframework.web.context.ConfigurableWebApplicationContext
	 */
	void setParent(ApplicationContext parent);

	/**
	 * Set the {@code Environment} for this application context.
	 * 设置应用上下文的环境
	 * @param environment the new environment
	 * @since 3.1
	 */
	void setEnvironment(ConfigurableEnvironment environment);

	/**
	 * Return the {@code Environment} for this application context in configurable
	 * form, allowing for further customization.
	 * 获取应用上下文的可配置环境。
	 * @since 3.1
	 */
	@Override
	ConfigurableEnvironment getEnvironment();

	/**
	 * 添加bean工厂后处理器BeanFactoryPostProcessor,在任何bean的定义被评估之前,应用上下文刷新时,
	 * 将会应用bean工厂后处理器到内部的bean工厂。在上下文配置的过程中,调用。
	 * @param postProcessor the factory processor to register
	 */
	void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);

	/**
	 * 添加应用监听器,当上下文事件发生时,将会被通知,比如上下文刷新,上下文关闭事件。
	 * 需要注意是,如果上下文还没有激活,或者上下文事件已经激活,当前事件正在多播,当刷新的时候,已经注册到上下文的
	 * 监听器将会被通知。
	 * @param listener the ApplicationListener to register
	 * @see org.springframework.context.event.ContextRefreshedEvent
	 * @see org.springframework.context.event.ContextClosedEvent
	 */
	void addApplicationListener(ApplicationListener<?> listener);

	/**
	 * 注册当前上下文的给定协议解决器,允许额外的资源协议被处理。
	 * @since 4.3
	 */
	void addProtocolResolver(ProtocolResolver resolver);

	/**
	 * 加载或刷新配置的持久化表示层,可以是一个XML文件,属性文件,或者相关数据schema。如果startup方法失败,
	 * 应该销毁所有已经创建的单例bean,以便资源的空置。换句话,在调用启动方法后,所有的bean要么初始化,要么没有初始化。
	 * @throws BeansException if the bean factory could not be initialized
	 * 如果bean工厂不能够初始化,则抛出BeansException异常
	 * @throws IllegalStateException if already initialized and multiple refresh
	 * attempts are not supported
	 * 如果已经初始化且多次尝试刷新不支持,则抛出IllegalStateException异常。
	 */
	void refresh() throws BeansException, IllegalStateException;

	/**
	 * 注意一个JVM运行时关闭hook,在虚拟机关闭时,关闭当前上下文,除非上下文已经关闭。
	 * 此方法可以被调用多次。每个上下文实例,最多注意一个关闭hook。
	 * @see java.lang.Runtime#addShutdownHook
	 * @see #close()
	 */
	void registerShutdownHook();

	/**
	 * 关闭当前应用上下文,释放上下文关联的资源和持有的锁。包括销毁所有缓存的单例bean。
	 * 需要注意的是:不会调用父上下文的关闭方法,因为父上下文有自己独立的声明周期。
	 * 此方法可以调用多次,如果上下文已经关闭,则忽略。
	 */
	@Override
	void close();

	/**
	 * 判断当前上下文是否激活,也就是上下文是否至少刷新一次并且没有归案必。
	 * @return whether the context is still active
	 * @see #refresh()
	 * @see #close()
	 * @see #getBeanFactory()
	 */
	boolean isActive();

	/**
	 * 返回上下文内部bean工厂。可以用于访问底层工厂的相关功能。
	 * 需要注意的是:不要使用bean工厂的后处理器;因为单实例bean已经初始化。在bean可用之前,可以使用bean工厂后处理器,
	 * 拦截bean工厂的设置过程。
	 * 一般情况下,当上下文处于激活状态,内部的bean工厂是可以访问的,也就是在{@link #refresh()} 和 {@link #close()}.
	 * 方法之前的情况,{@link #isActive()可用于检查上下文是否处于合适的状态。
	 * @return the underlying bean factory
	 * 底层的bean工厂
	 * @throws IllegalStateException if the context does not hold an internal
	 * bean factory (usually if {@link #refresh()} hasn't been called yet or
	 * if {@link #close()} has already been called)
	 * 如果上下文还没有持有内部bean工厂,则抛出IllegalStateException异常。即{@link #refresh()}还
	 * 没有调用,或{@link #close()}方法已经调用。
	 * @see #isActive()
	 * @see #refresh()
	 * @see #close()
	 * @see #addBeanFactoryPostProcessor
	 */
	ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

}

从上可以看出,ConfigurableApplicationContext具备应用上下文 ApplicationContex 相关操作以外,同时具有了生命周期和流属性。除此之外, 提供了设置应用id,设置父类上下文,设置环境 ConfigurableEnvironment,添加应用监听器,添加bean工厂后处理器 BeanFactoryPostProcessor,添加协议解决器 ProtocolResolver,刷新应用上下文,关闭应用上下文,判断上下文状态,以及注册虚拟机关闭Hook等操作,同时重写了获取环境操作,此操作返回的为可配置环境 ConfigurableEnvironment。最关键的是提供了获取内部bean工厂的访问操作, 方法返回为 ConfigurableListableBeanFactory。需要注意的是,调用关闭操作,并不关闭父类的应用上下文,应用上下文与父类的上下文生命周期,相互独立。
从ConfigurableApplicationContext的方法,而已看出ConfigurableApplicationContext主要实现的是set相关的操作,而ApplicationContext是get相关的操在,我们可以简单的理解为读写操作分离。
下面我们依次来看一下ConfigurableApplicationContext接口关联的接口,BeanFactoryPostProcessorProtocolResolverConfigurableEnvironmentConfigurableListableBeanFactory

我们先来看BeanFactoryPostProcessor接口。

BeanFactoryPostProcessor

源码参见:BeanFactoryPostProcessor

/*
 * Copyright 2002-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.beans.factory.config;

import org.springframework.beans.BeansException;

/**
 *BeanFactoryPostProcessor后处理器,允许对应用上下文的bean定义进行一般的修改,调整底层bean工厂
 *上下文中的bean属性。
 *应用上下文可以自动探测,在上下文中的bean工厂后处理器BeanFactoryPostProcessor bean的定义,
 *在其他任何bean在创建前,应用bean工厂后处理器。
 *bean工厂后处理器在系统管理级的配置文件非常有用,用于重写应用上下文中的配置属性。
 *具体查看属性资源配置及其具体的实现,是一种开箱即用的加强配置的解决方案。
 * bean工厂后处理器,修改bean定义或与之交互,而不是bean的实例。这样也许引起bean的过早初始化,
 * 违背的bean容器的原则,进而带来意想不到的影响。如果需要与bean的实例进行交互,
 * 可以使用bean后处理器替代BeanPostProcessor。
 *
 * @author Juergen Hoeller
 * @since 06.07.2003
 * @see BeanPostProcessor
 * @see PropertyResourceConfigurer
 */
public interface BeanFactoryPostProcessor {

	/**
	 * 在上下文标准初始化完毕后,修改应用上下文的内部bean工厂。所有的bean定义都将加载,
	 * 但是没有bean已经被初始化。允许重写或添加属性到将要初始化的bean。
	 * @param beanFactory the bean factory used by the application context
	 * 上下文bean工厂正在使用的bean工厂。
	 * @throws org.springframework.beans.BeansException in case of errors
	 */
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

从上面可以看出,bean工厂后处理器BeanFactoryPostProcessor,主要提供了修改上下文的内部bean工厂操作,可以修改bean的定义, 而不能修改bean的实例属性。如果要与bean的实例交互,可以实现bean后处理器BeanPostProcessor。bean工厂后处理器一般用系统级的配置,比如 PropertyResourceConfigurer,并重写bean定义的属性。

ProtocolResolver

源码参见:ProtocolResolver

package org.springframework.core.io;

/**
 *ProtocolResolver接口是一个特殊协议资源的解决策略处理器。
 *默认资源加载器的系统包接口SPI,在没有子类资源加载器和应用上下文的实现的情况下,
 *运行处理一般的资源协议。
 * @author Juergen Hoeller
 * @since 4.3
 * @see DefaultResourceLoader#addProtocolResolver
 */
public interface ProtocolResolver {

	/**
	 * 如果协议的实现匹配,则根据资源加载器,解决给定位置的资源。
	 * @param location the user-specified resource location
	 * @param resourceLoader the associated resource loader
	 * @return a corresponding {@code Resource} handle if the given location
	 * matches this resolver's protocol, or {@code null} otherwise
	 */
	Resource resolve(String location, ResourceLoader resourceLoader);

}

从上面可以看出,ProtocolResolver接口,主要提供了根据资源位置加载相应资源的操作,在没有子类资源加载器和应用上下文的实现的情况下,默认资源加载器 DefaultResourceLoader 的系统包接口SPI,允许处理一般的资源协议。

ConfigurableEnvironment

源码参见:ConfigurableEnvironment

package org.springframework.core.env;

import java.util.Map;

/**
 *配置环境接口ConfigurableEnvironment是大多数环境类型需要实现的配置接口。为设置激活和默认配置,及操纵底层属性源提供了
 *便利。允许客户端通过定制转换服务设置和校验需要的属性,更多的通过{@link ConfigurablePropertyResolver}。
 * 操纵属性源。
 * 属性源可以被移除,重排序或替换;另外属性源可以通过 {@link #getPropertySources()}方法返回的{@link MutablePropertySources}
 * 添加到环境中。下面是一个可配置环境的标准实现{@link StandardEnvironment},尽管一些特殊的默认属性源不同,但一般情况下,适合所有实现。
 *
 * <h4>Example: adding a new property source with highest search priority</h4>
 * 添加一个最高优先级的属性源
 * <pre class="code">
 * ConfigurableEnvironment environment = new StandardEnvironment();
 * MutablePropertySources propertySources = environment.getPropertySources();
 * Map<String, String> myMap = new HashMap<String, String>();
 * myMap.put("xyz", "myValue");
 * propertySources.addFirst(new MapPropertySource("MY_MAP", myMap));
 * </pre>
 *
 * <h4>Example: removing the default system properties property source</h4>
 * 移除默认系统属性源。
 * <pre class="code">
 * MutablePropertySources propertySources = environment.getPropertySources();
 * propertySources.remove(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)
 * </pre>
 *
 * <h4>Example: mocking the system environment for testing purposes</h4>
 * mock系统环境
 * <pre class="code">
 * MutablePropertySources propertySources = environment.getPropertySources();
 * MockPropertySource mockEnvVars = new MockPropertySource().withProperty("xyz", "myValue");
 * propertySources.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, mockEnvVars);
 * </pre>
 *
 * 当一个环境被应用上下文使用时,比较重要的是,{@code PropertySource}的所有操作必须在{@link
 * org.springframework.context.support.AbstractApplicationContext#refresh() refresh()}
 * 调用之前。这可以确保,在容器启动的过程中,所有的属性源都可用,包括{@linkplain
 * org.springframework.context.support.PropertySourcesPlaceholderConfigurer property
 * placeholder configurers}.
 *
 * @author Chris Beams
 * @since 3.1
 * @see StandardEnvironment
 * @see org.springframework.context.ConfigurableApplicationContext#getEnvironment
 */
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {

	/**
	 * 设置环境的激活配置集。在容器启动的过程中,可以根据配置来决定是否将bean定义注册到容器中。
	 * <p>Any existing active profiles will be replaced with the given arguments; call
	 * 任何已经存在的激活配置,将会被参数指定的配置集替代;当参数为0时,则清除当前激活的配置。如果先要保护已经激活的
	 * 配置集,可以使用{@link #addActiveProfile}方法。
	 * @see #addActiveProfile
	 * @see #setDefaultProfiles
	 * @see org.springframework.context.annotation.Profile
	 * @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
	 * @throws IllegalArgumentException if any profile is null, empty or whitespace-only
	 */
	void setActiveProfiles(String... profiles);

	/**
	 * 添加配置到当前激活的配置集。
	 * @see #setActiveProfiles
	 * @throws IllegalArgumentException if the profile is null, empty or whitespace-only
	 */
	void addActiveProfile(String profile);

	/**
	 * 设置默认的配置集
	 * 如果没有任何配置显示地通过{@link #setActiveProfiles}设置配置,则使用默认的配置集。
	 * @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
	 * @throws IllegalArgumentException if any profile is null, empty or whitespace-only
	 */
	void setDefaultProfiles(String... profiles);

	/**
	 * 返回当前环境的mutable形式属性源{@link PropertySources},当根据环境对象解决属性的时候,可以使用
	 * 属性源集合。
	 * 多样属性源{@link MutablePropertySources}的相关方法,如下,
	 * {@link MutablePropertySources#addFirst addFirst},
	 * {@link MutablePropertySources#addLast addLast},
	 * {@link MutablePropertySources#addBefore addBefore} and
	 * {@link MutablePropertySources#addAfter addAfter},
	 * 如果需要的话,我们可以控制这些属性源的顺序。这种 策略非常有用,比如,确保用户定义的属性源由系统属性集或者系统环境
	 * 变量集。
	 * @see AbstractEnvironment#customizePropertySources
	 */
	MutablePropertySources getPropertySources();

	/**
	 * 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)}.
	 * 如果当前安全管理器允许,返回系统环境变量{@link System#getenv()}的值,否则将尝试使用{@link System#getenv(String)},
	 * 方法获取每个key的属性值,再放入到Map集合中。
	 * 需要注意的是,大多数的{@link Environment}的实现,将会包括系统环境变量Map作为一个可以搜索的属性源PropertySource。
	 * 因此强烈建议,除非有明确的需要添加其他属性源,否则此方法不建议直接调用。
	 * 调用{@link Map#get(Object)}方法,不会返回一个非法访问异常;比如当安全管理禁止访问属性值,null将会返回,
	 * 同时一个INFO级的日志信息将会通知这个异常。
	 */
	Map<String, Object> getSystemEnvironment();

	/**
	 * 如果当前安全管理器允许,将返回系统属性{@link System#getProperties()}的值,否则将调用{@link System#getProperty(String)}.
	 * 方法获取每个key的值,添加的结果集中。
	 * 需要注意的是,大多数的{@link Environment}的实现,将会包括系统属性Map作为一个可以搜索的属性源PropertySource。
	 * 因此强烈建议,除非有明确的需要添加其他属性源,否则此方法不建议直接调用。
	 * 调用{@link Map#get(Object)}方法,不会返回一个非法访问异常;比如当安全管理禁止访问属性值,null将会返回,
	 * 同时一个INFO级的日志信息将会通知这个异常。
	 */
	Map<String, Object> getSystemProperties();

	/**
	 * 添加给定父类环境激活配置,默认配置和属性源到当前环境(child)的各自的集合中。
	 * 在父类和子类中,如果任何相同命名的属性源实例已经存在,则子类的属性源实例将会保留,父类的实例将会被丢弃。
	 * 通过这种方法,运行子类重新父类的属性源,可以避免通过一般属性源类型冗余的搜索。比如系统环境变量和系统属性。
	 * 激活和默认的配置名也将过滤,以避免多余副本的存在,引起冲突。
	 * 在任何情况下父类的环境是不可修改的。注意,在调用{@code merge}方法后,任何父类环境修改的发生,将不会影响其子类的环境。
	 * 因此,在调用{@code merge}方法前,我们应该优先配置父类的属性源和配置信息。
	 * @param parent the environment to merge with
	 * @since 3.1.2
	 * @see org.springframework.context.support.AbstractApplicationContext#setParent
	 */
	void merge(ConfigurableEnvironment parent);

}

从上面可以看出,ConfigurableEnvironment接口提供设置、添加环境配置,设置默认配置,获取属性源 MutablePropertySources,获取系统环境变量,获取系统属性,及合并环境。注意设置环境配置,会覆盖先前的配置,如果要添加配置到当前配置集,要使用添加环境配置方法。另外需要注意的是,父类的环境是不可修改,在调用{@code merge}方法前,我们应该优先配置父类的属性源和配置信息,同时子类的环境配置中的属性源将会时父类中的同名数据源失效。

ConfigurableEnvironment提供的是环境配置的set相关操作,Environment提供的是环境配置的get相关操作,可以简单理解为读写分离。

关于 MutablePropertySources ,如果篇幅够的话,我们会在这篇讲,否则将放在后续文章中。

在来看一下ConfigurableEnvironment的父类接口ConfigurablePropertyResolver

ConfigurablePropertyResolver

源码参见:ConfigurablePropertyResolver

package org.springframework.core.env;

import org.springframework.core.convert.support.ConfigurableConversionService;

/**
 *ConfigurablePropertyResolver是{@link PropertyResolver}的实现必须实现接口。当需要转化一个类型到另外一个类型时,
 *为访问,和定制类型转换服务的便利。
 * @author Chris Beams
 * @since 3.1
 */
public interface ConfigurablePropertyResolver extends PropertyResolver {

	/**
	 * 当执行属性类型转换时,返回使用的可配转换服务ConfigurableConversionService。
	 * 可配转换服务为添加和移除特殊的转换器实例,提供了便利。
	 * <pre class="code">
	 * ConfigurableConversionService cs = env.getConversionService();
	 * cs.addConverter(new FooConverter());
	 * </pre>
	 * @see PropertyResolver#getProperty(String, Class)
	 * @see org.springframework.core.convert.converter.ConverterRegistry#addConverter
	 */
	ConfigurableConversionService getConversionService();

	/**
	 *设置可以执行类型转换的ConfigurableConversionService
	 * 需要注意的是,如果我们想替换ConversionService,添加或移除类型转换器,可以从{@link #getConversionService()},
	 * 获取可配类型转换服务,比如{@code #addConverter}。
	 * @see PropertyResolver#getProperty(String, Class)
	 * @see #getConversionService()
	 * @see org.springframework.core.convert.converter.ConverterRegistry#addConverter
	 */
	void setConversionService(ConfigurableConversionService conversionService);

	/**
	 * 设置占位符前缀
	 */
	void setPlaceholderPrefix(String placeholderPrefix);

	/**
	 * 设置占位符后缀
	 */
	void setPlaceholderSuffix(String placeholderSuffix);

	/**
	 * 设置被解决器替代占位符和其默认值之间的分割符,如果在处理的过程中,没有这个特殊分割符,则将是使用默认的或null最为分割符。
	 */
	void setValueSeparator(String valueSeparator);

	/**
	 * 此方法用于设置,当遇见一个不能解决的嵌入在给定属性的占位符时,是否抛出异常。如果为false,则预示者,严格控制
	 * 属性解决,比如抛出一个异常。true表示,如果没有解决${...}形式的属性的,则通过。
	 * {@link #getProperty(String)}方法的实现,和他的变体,必须监视值集,已决定当属性值集包含未解决的占位符时,
	 * 纠正占位符属性。
	 * @since 3.2
	 */
	void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);

	/**
	 * 设置需要校验的属性
	 * {@link #validateRequiredProperties()}.
	 */
	void setRequiredProperties(String... requiredProperties);

	/**
	 * 校验每个需要校验的属性,及{@link #setRequiredProperties}设置的属性,并解决非空值。
	 * @throws MissingRequiredPropertiesException if any of the required
	 * properties are not resolvable.
	 */
	void validateRequiredProperties() throws MissingRequiredPropertiesException;

}

从上面可以看出,ConfigurablePropertyResolver,主要提供了设置和获取可配类型转换器服务 ConfigurableConversionService,设置属性占位符前缀和后缀,设置需要校验的属性,校验需要校验的属性操作,同时,提供了设置当占位符属性,无法解决时,是否抛出异常,设置被解决器替代占位符和其默认值之间的分割符操作。

可配类型转换服务ConfigurableConversionService接口,我们在后续的文章中,将会去单独将这个接口。
今天我们先看到这里,本身想在看一下ConfigurableListableBeanFactory,一看定义,相关知识比较多,我们就放在另外一篇文章中再讲。

我们以ConfigurableApplicationContext的类图,结束这篇的文章。 ConfigurableApplicationContext

总结

Lifecycle接口提供了启动和关闭操作,以及判断当前组件是否运行操作。需要注意的是启动和停止操作,将会传播给容器中的所有子容器中的组件。对于停止操作,不保证停止通知发生在析构之前。对于判断当前组件是否运行操作,如果组件是容器,只有在容器中所有组件包括子容器中的组件,都在运行的情况下,才返回true。

ConfigurableApplicationContext具备应用上下文 ApplicationContex 相关操作以外,同时具有了生命周期和流属性。除此之外, 提供了设置应用id,设置父类上下文,设置环境 ConfigurableEnvironment,添加应用监听器,添加bean工厂后处理器 BeanFactoryPostProcessor,添加协议解决器 ProtocolResolver,刷新应用上下文,关闭应用上下文,判断上下文状态,以及注册虚拟机关闭Hook等操作,同时重写了获取环境操作,此操作返回的为可配置环境 ConfigurableEnvironment。最关键的是提供了获取内部bean工厂的访问操作, 方法返回为 ConfigurableListableBeanFactory。需要注意的是,调用关闭操作,并不关闭父类的应用上下文,应用上下文与父类的上下文生命周期,相互独立。

从ConfigurableApplicationContext的方法,而已看出ConfigurableApplicationContext主要实现的是set相关的操作,而ApplicationContext是get相关的操在,我们可以简单的理解为读写操作分离。

bean工厂后处理器BeanFactoryPostProcessor,主要提供了修改上下文的内部bean工厂操作,可以修改bean的定义, 而不能修改bean的实例属性。如果要与bean的实例交互,可以实现bean后处理器BeanPostProcessor。bean工厂后处理器一般用系统级的配置,比如 PropertyResourceConfigurer,并重写bean定义的属性。

ProtocolResolver接口,主要提供了根据资源位置加载相应资源的操作,在没有子类资源加载器和应用上下文的实现的情况下,默认资源加载器 DefaultResourceLoader 的系统包接口SPI,允许处理一般的资源协议。

ConfigurableEnvironment接口提供设置、添加环境配置,设置默认配置,获取属性源 MutablePropertySources,获取系统环境变量,获取系统属性,及合并环境。注意设置环境配置,会覆盖先前的配置,如果要添加配置到当前配置集,要使用添加环境配置方法。另外需要注意的是,父类的环境是不可修改,在调用{@code merge}方法前,我们应该优先配置父类的属性源和配置信息,同时子类的环境配置中的属性源将会时父类中的同名数据源失效。

ConfigurableEnvironment提供的是环境配置的set相关操作,Environment提供的是环境配置的get相关操作,可以简单理解为读写分离。

ConfigurablePropertyResolver,主要提供了设置和获取可配类型转换器服务 ConfigurableConversionService,设置属性占位符前缀和后缀,设置需要校验的属性,校验需要校验的属性操作,同时,提供了设置当占位符属性,无法解决时,是否抛出异常,设置被解决器替代占位符和其默认值之间的分割符操作。

应用上下文相关事件:

ContextRefreshedEvent

package org.springframework.context.event;

import org.springframework.context.ApplicationContext;

/**
 * Event raised when an {@code ApplicationContext} gets initialized or refreshed.
 *
 * @author Juergen Hoeller
 * @since 04.03.2003
 * @see ContextClosedEvent
 */
@SuppressWarnings("serial")
public class ContextRefreshedEvent extends ApplicationContextEvent {

	/**
	 * Create a new ContextRefreshedEvent.
	 * @param source the {@code ApplicationContext} that has been initialized
	 * or refreshed (must not be {@code null})
	 */
	public ContextRefreshedEvent(ApplicationContext source) {
		super(source);
	}
}

ContextClosedEvent

package org.springframework.context.event;

import org.springframework.context.ApplicationContext;

/**
 * Event raised when an {@code ApplicationContext} gets closed.
 * @author Juergen Hoeller
 * @since 12.08.2003
 * @see ContextRefreshedEvent
 */
@SuppressWarnings("serial")
public class ContextClosedEvent extends ApplicationContextEvent {

	/**
	 * Creates a new ContextClosedEvent.
	 * @param source the {@code ApplicationContext} that has been closed
	 * (must not be {@code null})
	 */
	public ContextClosedEvent(ApplicationContext source) {
		super(source);
	}
}

ApplicationContextEvent

package org.springframework.context.event;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;

/**
 * Base class for events raised for an {@code ApplicationContext}.
 *
 * @author Juergen Hoeller
 * @since 2.5
 */
@SuppressWarnings("serial")
public abstract class ApplicationContextEvent extends ApplicationEvent {

	/**
	 * Create a new ContextStartedEvent.
	 * @param source the {@code ApplicationContext} that the event is raised for
	 * (must not be {@code null})
	 */
	public ApplicationContextEvent(ApplicationContext source) {
		super(source);
	}

	/**
	 * Get the {@code ApplicationContext} that the event was raised for.
	 */
	public final ApplicationContext getApplicationContext() {
		return (ApplicationContext) getSource();
	}

}