源码
/**
* @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 {
String[] getActiveProfiles();
String[] getDefaultProfiles();
@Deprecated
boolean acceptsProfiles(String... profiles);
boolean acceptsProfiles(Profiles profiles);
}
据类上注释:
一,接口表示当前应用程序运行的环境。两个关键配置为:profiles和properties配置文件。属性访问通过PropertyResolver接口中相关的方法。所谓的profiles就类似于@Profile(“dev”)或者properties配置文件中的spring.profiles.active=dev。Environment通过getActiveProfiles知道当前激活那些配置文件,通过getDefaultProfiles知道那些被默认激活。
二,在ApplicationContext中管理的bean可以
1. 注册为EnvironmentAware,这里我理解为继承EnvironmentAware接口,并且实现其setEnvironment方法; EnvironmentAware详细说明
2. 通过@Inject 或者@Autowired注解 注入Environment;@Inject详细说明
这可以让我们直接访问Environment,然后访问配置文件中的内容
三,然而,在大多数情况下,应用程序级bean不需要与直接Environment交互而是使用占位符${…} 访问,这是通过PropertySourcesPlaceholderConfigurer 实现的,它本身是EnvironmentAware,并且在纯Spring 3.1之后在使用context:property-placeholder/时默认注册,而在springboot会默认注册。
四,Environment的配置必须通过ConfigurableEnvironment接口,该接口从所有AbstractApplicationContext的getEnvironment方法返回。
然后介绍相关类
PropertyResolver
Environment继承了PropertyResolver属性解析器,用来解析配置文件中的属性。
public interface PropertyResolver {
boolean containsProperty(String key);
@Nullable
String getProperty(String key);
String getProperty(String key, String defaultValue);
@Nullable
<T> T getProperty(String key, Class<T> targetType);
<T> T getProperty(String key, Class<T> targetType, T defaultValue);
String getRequiredProperty(String key) throws IllegalStateException;
<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;
String resolvePlaceholders(String text);
String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
}
这些方法很简单,就是用来 获取属性。
EnvironmentCapable
public interface EnvironmentCapable {
/**
* Return the {@link Environment} associated with this component.
*/
Environment getEnvironment();
}
显然,这个接口只有一个方法;
所有Spring应用程序上下文继承了EnvironmentCapable,接口主要用于在框架方法中执行instanceof检查,以便与环境交互(如果环境确实可用的话)。(注意ApplicationContext继承了ListableBeanFactory,而ListableBeanFactory继承了BeanFactory,但是继承了BeanFactory的未必是ApplicationContext,而继承EnvironmentCapable的一定是ApplicationContext)
ApplicationContext继承了EnvironmentCapable,从而继承了getenenvironment()方法; ConfigurableApplicationContext继承了ApplicationContext并重新定义了getEnvironment并缩小签名以返回一个ConfigurableEnvironment而非其父接口Environment。从ConfigurableApplicationContext访问Environment对象时,它都是“只读”的,此时它也可以被配置。(这个只读我不是很理解)
ConfigurableEnvironment
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {
//设置活动的配置文件
void setActiveProfiles(String... profiles);
// 增加活动的配置文件
void addActiveProfile(String profile);
// 设置默认的配置文件
void setDefaultProfiles(String... profiles);
// 获取PropertySource键值组合的集合
MutablePropertySources getPropertySources();
// 系统环境变量
Map<String, Object> getSystemEnvironment();
// 系统配置
Map<String, Object> getSystemProperties();
// 合并
void merge(ConfigurableEnvironment parent);
}
- 由大多数Environment类型实现的配置接口,也就是说,一般而言,不会直接继承Environment,而是继承ConfigurableEnvironment
- 可以设置活动的和默认活动的profiles文件(如setActiveProfiles和setDefaultProfiles)
- 可以操作底层属性源。配置文件的解析出来的数据,也是封装成了PropertySource放在ConfigurableEnvironment中。
- 允许客户端通过ConfigurablePropertyResolver接口设置(setRequiredProperties)和验证(validateRequiredProperties)所需的属性,自定义转换服务(setConversionService)等。
操作属性源:
5. 属性源可以被删除、重新排序或替换;并且可以使用从getPropertySources返回的MutablePropertySources实例添加其他属性源。
6. 下面的示例是针对ConfigurableEnvironment的StandardEnvironment实现的,但一般适用于任何实现,尽管特定的默认属性源可能不同。
1. 添加具有最高搜索优先级的新属性源:
ConfigurableEnvironment environment = new StandardEnvironment();
MutablePropertySources propertySources = environment.getPropertySources();
Map<zString,String> myMap = new HashMap();
myMap.put("xyz", "myValue");
propertySources.addFirst(new MapPropertySource("MY_MAP", myMap));
2. 删除默认的系统属性属性源
MutablePropertySources propertySources = environment.getPropertySources();
propertySources.remove(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)
3. 模拟系统环境以进行测试
MutablePropertySources propertySources = environment.getPropertySources();
MockPropertySource mockEnvVars = new MockPropertySource().withProperty("xyz", "myValue");
propertySources.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, mockEnvVars);
当ApplicationContext使用Environment时,在调用refresh方法(源自AbstractApplicationContext)之前执行此类PropertySource操作是很重要的。这确保了容器引导过程中所有属性源都是可用的,包括PropertySourcesPlaceholderConfigurer所使用的属性源。
AbstractEnvironment
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore";
public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";
public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";
protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";
protected final Log logger = LogFactory.getLog(getClass());
private final Set<String> activeProfiles = new LinkedHashSet<String>();
private final Set<String> defaultProfiles = new LinkedHashSet<String>(getReservedDefaultProfiles());
private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
// 自定义PropertySource, 交给子类去实现
protected void customizePropertySources(MutablePropertySources propertySources) {
}
}
- 支持保留默认配置文件名称的概念,并支持通过ACTIVE_PROFILES_PROPERTY_NAME和DEFAULT_PROFILES_PROPERTY_NAME属性指定活动的和默认活动的配置文件。
- 具体子类的区别主要在于AbstractEnvironment默认添加PropertySource对象(在无参构造中)。不过这个交给子类实现
- 子类应该通过受保护的customizePropertySources(MutablePropertySources)钩子提供属性源也就是MutablePropertySources ,而客户端应该使用继承自ConfigurableEnvironment的getPropertySources()方法进行定制,并使用MutablePropertySources API。具体示例看上一小结
StandardEnvironment
public class StandardEnvironment extends AbstractEnvironment {
/** System environment property source name: {@value}. */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
/** JVM system properties property source name: {@value}. */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
}
- 除了ConfigurableEnvironment的常用功能之外,增加了属性解析和配置文件相关的操作。
- 配置了两个默认属性源,按以下顺序搜索:getSystemProperties getSystemEnvironment(继承自AbstractEnvironment),也就是说,如果键“xyz”在JVM系统属性和当前进程的环境变量集中都存在,那么调用{environment. getproperty(“xyz”)}返回的是系统属性中的键“xyz”的值。
这种顺序是默认选择的,因为系统属性是每个jvm的,而在给定系统上的许多jvm上的环境变量可能是相同的。给予系统属性优先级允许在每个jvm的基础上覆盖环境变量。如下面的代码 - 这些默认属性源可以被删除、重新排序或替换;
- 其他属性源可以使用可从getPropertySources() 获得的MutablePropertySources实例添加
- 有关特殊处理的详细信息,请参阅SystemEnvironmentPropertySource javadoc
在shell环境中不允许在变量名中使用句点字符的属性名(例如Bash)
@RestController
public class EnvironmentController {
@Autowired
Environment environment;
@GetMapping("testEnvironment")
public String test(){
System.setProperty("server.servlet.context-path","/Second");
System.out.println(environment.resolvePlaceholders("${server.servlet.context-path}"));
return environment.getProperty("server.servlet.context-path");
}
}
返回的是/Second而不是/First
EnvironmentAware
public interface EnvironmentAware extends Aware {
void setEnvironment(Environment environment);
}
接口被一些bean实现。setEnvironment方法在ConfigurableApplicationContext接口中也自定义了
ApplicationContext
所有的ApplicationContext都继承了EnvironmentCapable,所以都有getEnvironment方法
在ConfigurableApplicationContext中
//自定义,非继承
void setEnvironment(ConfigurableEnvironment environment);
@Override
ConfigurableEnvironment getEnvironment();
在AbstractApplicationContext中
@Override
public void setEnvironment(ConfigurableEnvironment environment) {
this.environment = environment;
}
@Override
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
//自定义,非继承
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
显然,系统中默认的Environment是StandardEnvironment;如果要详细了解AbstractApplicationContext 或者 ConfigurableApplicationContext,请看ApplicationContext
StandardEnvironment
public class StandardEnvironment extends AbstractEnvironment {
/** System environment property source name: {@value}. */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
/** JVM system properties property source name: {@value}. */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
//实现了AbstractEnvironment 中的方法,是自定义属性源 添加属性到propertySources
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
}