好的,各位观众老爷,各位技术大咖,还有各位和我一样在代码海洋里挣扎的小伙伴们,今天咱们来聊一个Spring Framework里“闷骚”但又极其重要的家伙——Environment抽象。
先别急着打哈欠,我知道“抽象”这两个字一出来,很多人就开始犯困。但信我,这玩意儿绝对值得你花时间搞清楚,因为它就像是Spring王国里的情报局长,掌握着各种机密信息,能让你在配置和应用中游刃有余。?
一、啥是Environment?别跟我扯抽象概念!
咱们先别急着啃那些官方文档里晦涩难懂的定义。用大白话说,Environment就是一个接口,它代表着应用程序运行时的环境。这个环境里包含了各种各样的属性,比如:
- 系统属性 (System Properties): 你电脑的操作系统版本,Java版本,用户名等等。
- 环境变量 (Environment Variables): 那些你手动配置的,或者系统自动设置的环境变量,比如JAVA_HOME,PATH等等。
- Profile: Spring里用来区分不同环境的“标签”,比如开发环境(dev),测试环境(test),生产环境(prod)等等。
- PropertySource: 这是Environment的核心组成部分,它代表着属性的来源,比如properties文件,YAML文件,甚至是数据库。
你可以把Environment想象成一个巨大的“百宝箱”,里面装着各种各样的“宝贝”,这些宝贝就是应用程序运行时需要的各种配置信息。
举个栗子:
假设你的应用程序需要连接数据库,那么数据库的URL,用户名,密码这些信息,就可以放在一个properties文件里,然后通过PropertySource加载到Environment中。
再举个栗子:
你想在开发环境和生产环境中使用不同的配置,就可以定义不同的Profile,然后在不同的环境下激活不同的Profile。
总而言之,Environment的作用就是提供一个统一的接口,让你能够方便地访问和管理应用程序的各种配置信息。
二、Environment接口:长啥样?
光说不练假把式,咱们来看看Environment接口长啥样。打开你的IDE,找到org.springframework.core.env.Environment接口,你会发现它其实很简单:
public interface Environment extends PropertyResolver {
/**
* Return the set of profiles explicitly made active for this environment.
*
* @see #acceptsProfiles
*/
String[] getActiveProfiles();
/**
* Return the set of profiles to be activated when no active profiles have been specified.
*
* @see #acceptsProfiles
*/
String[] getDefaultProfiles();
/**
* Return whether one or more of the given profiles is active or, in the case of no explicit active profiles,
* whether one or more of the given profiles is included in the set of default profiles.
*
* @param profiles the profile(s) to test
*/
boolean acceptsProfiles(Profiles profiles);
}
可以看到,Environment接口继承了PropertyResolver接口,而PropertyResolver接口定义了获取属性值的方法:
public interface PropertyResolver {
/**
* Return whether the given property key is available for resolution.
*
* @param key the property name to check
* @return whether the property key is available for resolution
*/
boolean containsProperty(String key);
/**
* Return the property value associated with the given key,
* or {@code null} if the key cannot be resolved.
*
* @param key the property name to resolve
* @return the property value or {@code null} if none found
* @throws IllegalArgumentException if the given key is empty
* @throws NullPointerException if the given key is null
* @see #getProperty(String, String)
* @see #getProperty(String, Class)
*/
String getProperty(String key);
/**
* Return the property value associated with the given key,
* or the supplied {@code defaultValue} if the key cannot be resolved.
*
* @param key the property name to resolve
* @param defaultValue the default value to return if no value is found
* @return the property value or {@code defaultValue} if none found
* @throws IllegalArgumentException if the given key is empty
* @throws NullPointerException if the given key is null
* @see #getProperty(String)
* @see #getProperty(String, Class, Object)
*/
String getProperty(String key, String defaultValue);
/**
* Return the property value associated with the given key,
* or {@code null} if the key cannot be resolved.
*
* @param key the property name to resolve
* @param targetType the expected type of the property value
* @return the property value or {@code null} if none found
* @throws IllegalArgumentException if the given key is empty
* @throws NullPointerException if the given key is null
* @see #getProperty(String)
*/
<T> T getProperty(String key, Class<T> targetType);
/**
* Return the property value associated with the given key,
* or the supplied {@code defaultValue} if the key cannot be resolved.
*
* @param key the property name to resolve
* @param targetType the expected type of the property value
* @param defaultValue the default value to return if no value is found
* @return the property value or {@code defaultValue} if none found
* @throws IllegalArgumentException if the given key is empty
* @throws NullPointerException if the given key is null
* @see #getProperty(String, Object)
*/
<T> T getProperty(String key, Class<T> targetType, T defaultValue);
/**
* Resolve the given placeholder using the properties available through this resolver.
*
* @param text the String to resolve
* @return the resolved String
* @throws IllegalArgumentException if the given text is empty
* @throws NullPointerException if the given text is null
* @throws IllegalArgumentException if the placeholder cannot be resolved
*/
String resolvePlaceholders(String text);
/**
* Resolve the given String value recursively, replacing all placeholders with
* corresponding property values as defined in the current PropertyResolver.
*
* @param text the String to resolve
* @return the resolved String
* @throws IllegalArgumentException if the given text is empty
* @throws NullPointerException if the given text is null
* @throws IllegalArgumentException if the placeholder cannot be resolved
*/
String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
}
所以,Environment接口的核心功能就是:
- 获取激活的Profile (
getActiveProfiles()): 告诉你当前哪些Profile是激活的。 - 获取默认的Profile (
getDefaultProfiles()): 告诉你如果没有激活任何Profile,默认使用哪些Profile。 - 判断是否接受某个Profile (
acceptsProfiles()): 判断当前环境是否接受指定的Profile。 - 获取属性值 (
getProperty()): 根据属性名获取属性值,可以指定默认值和类型。 - 判断属性是否存在 (
containsProperty()): 判断是否存在某个属性。 - 解析占位符 (
resolvePlaceholders()和resolveRequiredPlaceholders()): 将字符串中的占位符替换为实际的属性值。
三、Environment的实现类:幕后英雄
Environment只是一个接口,真正干活的是它的实现类。Spring Framework提供了几个常用的Environment实现类:
StandardEnvironment: 这是最常用的Environment实现类,它会加载系统属性,环境变量,以及配置文件中的属性。ConfigurableEnvironment: 这是Environment接口的一个扩展,提供了更多的配置选项,比如可以添加和移除PropertySource,可以设置激活的Profile等等。StandardEnvironment就是ConfigurableEnvironment的一个实现。AnnotationConfigApplicationContext和AnnotationConfigWebApplicationContext: 这两个类是基于注解的Spring容器,它们内部也持有一个ConfigurableEnvironment实例,用来管理配置信息。AbstractEnvironment: 抽象类,实现了ConfigurableEnvironment接口,简化了Environment实现类的开发。
关系图:
graph TD
A[Environment] --> B[PropertyResolver]
A --> C[ConfigurableEnvironment]
C --> D[AbstractEnvironment]
D --> E[StandardEnvironment]
F[AnnotationConfigApplicationContext] --> C
G[AnnotationConfigWebApplicationContext] --> C
表格总结:
| 类名 | 描述 |
|---|---|
Environment |
接口,定义了访问应用程序运行时环境的方法。 |
PropertyResolver |
接口,定义了获取属性值的方法。 |
ConfigurableEnvironment |
接口,扩展了Environment接口,提供了更多的配置选项。 |
AbstractEnvironment |
抽象类,实现了ConfigurableEnvironment接口,简化了Environment实现类的开发。 |
StandardEnvironment |
常用的实现类,会加载系统属性,环境变量,以及配置文件中的属性。 |
AnnotationConfigApplicationContext |
基于注解的Spring容器,内部持有一个ConfigurableEnvironment实例。 |
AnnotationConfigWebApplicationContext |
基于注解的Spring Web容器,内部持有一个ConfigurableEnvironment实例。 |
四、PropertySource:属性的来源地
PropertySource是Environment的核心组成部分,它代表着属性的来源。Spring Framework提供了多种PropertySource的实现类,可以从不同的来源加载属性:
PropertiesPropertySource: 从Properties文件中加载属性。ResourcePropertySource: 从Resource中加载属性,可以是Properties文件,XML文件等等。MapPropertySource: 从Map中加载属性。SystemEnvironmentPropertySource: 从系统环境变量中加载属性。SystemPropertiesPropertySource: 从系统属性中加载属性。CommandLinePropertySource: 从命令行参数中加载属性。JndiPropertySource: 从JNDI中加载属性。
表格总结:
| 类名 | 描述