回顾
在上文中讲到了系统的环境变量和属性,system.env和system.properties.这是java的两个全局变量。也就是说在java的应用程序中,你在任何为止都能取到其中的值。
并且我们可以通过set方法将(k,v)放到这两个全局变量中。
并且我们也知道了配置文件几乎可以从任何地方来,任何方式来,具体看自己的需求是什么样了。
那我们现在来看看spring 是不是将配置放到了这两个变量中呢?
二、Spring框架如何读取配置文件?
我们先来看一段程序
@Autowired
Environment environment;
@Test
public void contextLoads() {
System.out.println(System.getProperty("redis.host"));
System.out.println(System.getenv("redis.host"));
System.out.println(environment.getProperty("redis.host"));
}
控制台输出结果
null
null
127.0.0.1
我们发现spring加载的配置文件并不会存到 System下的两个全局变量中,而是存到了一个 Environment 实例中。
那我们来看看 这个 Environment 是何方神圣,启动时加载默认配置以及系统配置。
Environment接口
Environment 这个接口可以说是贯穿了spring得全套源码体系。它继承了 PropertyResolver 所有加载得配置文件均在这个接口中。
包括System.getenv 和 System.getProperty 这两个全局变量也可以从这个接口中读取到。我们来实验下
@Autowired
Environment environment;
@Test
public void contextLoads() {
System.out.println(System.getProperty("redis.host"));
System.out.println(System.getenv("redis.host"));
System.out.println(environment.getProperty("redis.host"));
System.out.println(environment.getProperty("USERNAME"));
}
控制台输出
null
null
127.0.0.1
yaozhongjie
可以看到 USERNAME 这个是系统得变量,我们获取到了。
也就是说 spring得整个框架属性配置是围绕着 Environment 得接口来得。
我们打印了注入得类名发现实例化得是这个StandardEnvironment
下面是类图
项目启动时,先往把systemEnvironment 和systemProperties 放进了这个实例。当然还有 application 和 bootstrap 默认配置文件。
而自定义得配置文件是通过 Configuration注解或Component 注解
+propertySource 注解在bean初始化前后 进行加载得
AbstractApplicationContext.refresh() 方法中得invokeBeanFactoryPostProcessors(beanFactory);这一步进行初始化得。但是这个接口只提供了 取属性得方法,并未提供设置属性得方法哦。
StandardEnvironment类
@Autowired
Environment environment;
@Autowired
StandardEnvironment standardEnvironment;
@Test
public void contextLoads() {
System.out.println(System.getProperty("redis.host"));
System.out.println(System.getenv("redis.host"));
System.out.println(environment.getProperty("redis.host"));
System.out.println(environment.getProperty("USERNAME"));
System.out.println(environment.getClass());
System.out.println(environment);
MutablePropertySources propertySources = standardEnvironment.getPropertySources();
propertySources.forEach(System.out::println);
}
控制台打印
null
null
127.0.0.1
yaozhongjie
class org.springframework.core.env.StandardEnvironment
StandardEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[ConfigurationPropertySourcesPropertySource {name='configurationProperties'}, MapPropertySource {name='Inlined Test Properties'}, PropertiesPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.properties]'}, ResourcePropertySource {name='class path resource [redis.properties]'}]}
ConfigurationPropertySourcesPropertySource {name='configurationProperties'}
MapPropertySource {name='Inlined Test Properties'}
PropertiesPropertySource {name='systemProperties'}
OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}
RandomValuePropertySource {name='random'}
OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.properties]'}
ResourcePropertySource {name='class path resource [redis.properties]'}
可以到这个实际上存储得是不同类型得文件对象。
ResourcePropertySource 为资源类型
OriginTrackedMapPropertySource 应用原始配置
那么这个时候问题来了,我如果写了两个一样得key,放在不同得配置文件下。那么用environment 中会读出哪个呢?
新建个 mq.properties
redis.host=123123123
redis.password=mq
新建配置类如下
@Configuration
@PropertySource(value = "classpath:mq.properties")
@ConfigurationProperties(prefix = "redis")
@Data
public class MqConfig {
private String host;
private String password;
private int port;
}
测试类中打印
@Autowired
MqConfig mqConfig;
@Autowired
RedisConfig redisConfig;
@Test
public void contextLoads() {
System.out.println(redisConfig.getHost());
System.out.println(mqConfig.getHost());
}
控制台输出
127.0.0.1
127.0.0.1
发现mqConfig 取到得也是redis.properties 中得值,那么我们怎么取到mq.properties中得值呢?
代码如下
MutablePropertySources propertySources = standardEnvironment.getPropertySources();
String property = (String) propertySources.get("class path resource [mq.properties]").getProperty("redis.host");
System.out.println(property);
控制台输出这样子就取到了。
123123123
当然现实开发中还是不要做如此愚蠢得事情。