1. 什么是 Environment
Environment 是 spring 3.1 引入的一种概念,它内置的一种环境抽象,主要是用来描述 applicationContext 的运行时的一些配置信息和构件信息,比如包含 properties 和 profiles 的信息。通过${}占位符可以获取properties的属性信息,profile可以用来指定构件,构件可以理解为一组配置信息。ConfigurableEnvironment 即为可配置的环境,提供了set和get一系列方法用来操作当前所使用的profile。
2. 简单看下源码
2.1 PropertyResolver
这个接口它应该是处理占位符 ${} ,就是用来做配置属性值的获取和解析
public interface PropertyResolver {
// 检查所有的配置属性中是否包含指定key
boolean containsProperty(String key);
// 以String的形式返回指定的配置属性的值
String getProperty(String key);
// 带默认值的获取
String getProperty(String key, String defaultValue);
// 指定返回类型的配置属性值获取
<T> T getProperty(String key, Class<T> targetType);
// ......
// 解析占位符
String resolvePlaceholders(String text);
// ......
}
得出结论:**Environment**
可以获取配置元信息,同时也可以解析占位符的信息。
2.2 ConfigurableEnvironment
Configurable 前缀,脑子里第一时间出现的就是 get set 果然,确实是这样,它就是用编程式设置 profile 。
还有一个方法可以注意下:
MutablePropertySources getPropertySources();
点开 MutablePropertySources 它的源码,发现它的内部就是一个 List
public class MutablePropertySources implements PropertySources {
private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
}
结论:Mutable 开头的类名,通常可能是一个类型的 List 组合封装。
2.3 StandardEnvironment
StandardEnvironment 是 SpringFramework 中默认使用的标准运行时环境的抽象实现,不过它里面的方法实现的非常少,基本都是由 AbstractEnvironment
负责实现。在后面的原理部分,我们还会再见到它的,这里先留意一下就好。
3. 使用 Enviroment(了解即可)
3.1 获得 Enviroment
@Component
public class EnvironmentHolder {
@Autowired
Environment environment;
public void printEnvironment() {
System.out.println(environment);
}
}
public class EnvironmentQuickstartApplication {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(
"com.linkedbear.spring.environment.a_quickstart.bean");
EnvironmentHolder environmentHolder = ctx.getBean(EnvironmentHolder.class);
environmentHolder.printEnvironment();
}
}
执行:
StandardEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[PropertiesPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}]}
3.2 Environment获取配置属性的值
@Configuration
@ComponentScan("com.ideal.bean")
@PropertySource("propertysource/jdbc.properties")
public class EnvironmentPropertyConfiguration {
}
@Component
public class EnvironmentHolder {
@Autowired
Environment environment;
public void printEnvironment() {
System.out.println(Arrays.toString(environment.getDefaultProfiles()));
System.out.println(environment.getProperty("jdbc.url"));
}
}
执行
[default]
jdbc:mysql://localhost:3306/demo
4. Environment 稍深入
上面控制台中,其实打印了 Profiles 信息,但是我们其实没有主动配置,这是为什么呢?
4.1 Environment的默认profiles
进入到 Environment
的抽象实现 AbstractEnvironment
中,getDefaultProfiles
调用了 doGetDefaultProfiles
方法,这个设计在 SpringFramework 中大量出现和使用
@Override
public String[] getDefaultProfiles() {
return StringUtils.toStringArray(doGetDefaultProfiles());
}
4.2 Spring 中的方法命名习惯
在 Spring 中,我们常能看到带do 和不带 do 的一组方法,不带 do 开头的方法一般负责前置校验处理、返回结果封装,带 do 开头的方法是真正执行逻辑的方法(如 getBean
方法的底层会调用 doGetBean
来真正的寻找 IOC 容器的 bean ,createBean
会调用 doCreateBean
来真正的创建一个 bean )。
4.2.1 doGetDefaultProfiles的实现
public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";
protected Set<String> doGetDefaultProfiles() {
synchronized (this.defaultProfiles) {
// 取框架默认的profiles,并与当前的对比
if (this.defaultProfiles.equals(getReservedDefaultProfiles())) {
// 如果一致,则尝试从Environment中获取显式声明的profiles
String profiles = getProperty(DEFAULT_PROFILES_PROPERTY_NAME);
// 如果有显式声明,则覆盖原有的默认值
if (StringUtils.hasText(profiles)) {
setDefaultProfiles(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(profiles)));
}
}
return this.defaultProfiles;
}
}
看这个方法的实现,整体逻辑也不算复杂,关键是看它取框架默认的 profiles ,它其实就是取的 AbstractEnvironment
中内置的常量:
protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";
protected Set<String> getReservedDefaultProfiles() {
return Collections.singleton(RESERVED_DEFAULT_PROFILE_NAME);
}
4.2.2 覆盖默认的 profiles方法
上面的源码中,也可以看到,我们可以通过声明 **spring.profiles.default**
的配置,来覆盖 SpringFramework 中原有的默认 profiles ,一个比较常用的方法是在 jvm 的启动参数上添加:
-Dspring.profiles.default=aaa
4.3 补充
Environment 的解析配置属性值的底层是 PropertySourcesPropertyResolver 来处理