Environment为Spring体系中的环境上下文,给我们提供了profiles and properties功能。
1 profiles
profiles用来区分不同环境的配置,每一个profiles相当于一个bean的集合
1.1 示例
例:
@Component
@Profile("dev")
public class EnvBeanDev {
}
@Component
@Profile("prd")
public class EnvBeanPrd {
}
启动类:
public class AnnotationDemo1 {
public static void main(String[] args) {
// 设置激活的profile
System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME,"dev");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(context.getBean("bean1"));
System.out.println(context.getBean("envBeanDev"));
System.out.println(context.getBean("envBeanPrd"));
}
}
输出结果:
可以看到设置了激活的Profile后,指定Profile和未设置Profile的Bean会被加载。
1.2 profiles设置激活
Spring的profiles有两个变量可以配置
- spring.profiles.default 默认值,优先级低。当active没有配置时,使用此变量。
- spring.profiles.active 优先级高,指定当前容器使用哪个profile。
上面例子是通过在System中设置spring.profiles.active ,Profiles激活有多种设置方式。
- JAVA标准API:System.setProperty(“spring.profiles.active”, “dev”);//在启动容器之前,先指定环境中的profiles参数
- 设置JVM的启动参数: -Dspring.profiles.active=dev
- spring环境上下文:Environment.setActiveProfiles(“wow”,“pes”,“ff”);
- 等还有maven、web.xml、dipatecherServlet
1.3 原理
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
/**
* The set of profiles for which the annotated component should be registered.
*/
String[] value();
}
可以看到Profile的原理就是通过Conditional实现的,在扫描到Componment后会调用ProfileCondition的matches方法,比对当前Bean的profile和激活的profile是否相同。
2 properties
首先理解几个接口:
PropertySource:属性源,属性的来源,其中存放的是键-值对,比如System中的属性、properties文件中的属性,不同的properties文件对应不同的属性源。
PropertySources:管理属性源的接口,相当于是属性源的集合。
PropertyResolver:属性解析器定义了对属性的操作,存取属性,解析占位符。
Spring中默认的环境上线文是StandardEnvironment,StandardEnvironment继承了AbstractEnvironment,StandardEnvironment实例化的时候会调用父类构造方法,会调用父类无参构造,AbstractEnvironment类的构造函数如下:
public AbstractEnvironment() {
this(new MutablePropertySources());
}
protected AbstractEnvironment(MutablePropertySources propertySources) {
this.propertySources = propertySources;
this.propertyResolver = createPropertyResolver(propertySources);
customizePropertySources(propertySources);
}
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()));
}
无参构造中首先会创建一个PropertySources的实现MutablePropertySources管理PropertySource,相当于一个PropertySource集合,在第二个构造函数中还创建了PropertyResolver默认解析器PropertySourcesPropertyResolver,传入了属性源集合,最后调用customizePropertySources加入了两个属性源。
2.1 Environment#getProperty(String key)原理
该方法首先会进入AbstractEnvironment#getProperty(String key)
public String getProperty(String key) {
return this.propertyResolver.getProperty(key);
}
调用了解析器的getProperty,而上面我们知道默认的解析器为:PropertySourcesPropertyResolver,多以进入PropertySourcesPropertyResolver#getProperty(String key):
public String getProperty(String key) {
return getProperty(key, String.class, true);
}
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if (this.propertySources != null) {
// 循环所有的PropertySource获取对应属性的值
for (PropertySource<?> propertySource : this.propertySources) {
if (logger.isTraceEnabled()) {
logger.trace("Searching for key '" + key + "' in PropertySource '" +
propertySource.getName() + "'");
}
Object value = propertySource.getProperty(key);
if (value != null) {
if (resolveNestedPlaceholders && value instanceof String) {
// 解析占位符${}
value = resolveNestedPlaceholders((String) value);
}
logKeyFound(key, propertySource, value);
// 转换为指定类型
return convertValueIfNecessary(value, targetValueType);
}
}
}
if (logger.isTraceEnabled()) {
logger.trace("Could not find key '" + key + "' in any property source");
}
return null;
}
最终还是循环了所有PropertySources,从PropertySource中获取属性值。
2.2 向Environment中加入PropertySource
通过@PropertySource注解,该注解通过PropertySourceFactory的实现读取配置文件,所以如果是读取其它类的实现可自行实现。