理解Spring Environment抽象
- 统一的配置属性管理
Spring Framework3.1开始引入Environment抽象,它统一Spring配置属性的存储,包括占位符处理和类型转换,不仅完整地替换PropertyPlaceholderConfigurer,而且还支持更丰富的配置属性源(PropertySource)
- 条件化Spring Bean装配管理
通过Environment Profiles信息,帮助Spring容器提供条件化地装配Bean
Spring Environment接口使用场景
- 用于属性占位符处理
- 用于转换Spring配置属性类型
- 用于存储Spring配置属性源(PropertySource)
- 用于Profiles状态的维护
Environment占位符处理
- Spring3.1前占位符处理
- 组件:org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
- 接口:org.springframework.util.StringValueResolver
- Spring3.1+占位符处理
- 组件:org.springframework.context.support.PropertySourcesPlaceholderConfigurer
- 接口:org.springframework.beans.factory.config.EmbeddedValueResolver
理解条件配置Spring Profiles
-
Spring3.1条件配置
- API:org.springframework.core.env.ConfigurableEnvironment
- 修改:addActiveProfile(String)、setActiveProfiles(String…)、setDefaultActiveProfiles(String…)
- 获取:getActiveProfiles()和getDefaultProfiles()
- 匹配:acceptsProfiles(String…)和acceptsProfiles(Profiles)
- 注解:@org.springframework.context.annotation.Profile
- API:org.springframework.core.env.ConfigurableEnvironment
-
基于Spring4重构@Profile Condition接口
- org.springframework.context.annotation.ProfileCondition
依赖注入Environment
- 直接依赖注入
- 通过EnvironmentAware接口回调
- 通过@Autowire注入Environment
- 间接依赖注入
- 通过ApplicationContextAware接口回调
- 通过@Autowire注入ApplicationContext
依赖查找Environment
- 直接依赖查找
- 通过org.springframework.context.ConfigurableApplicationContext#ENVIRONMENT_BEAN_NAME
- 间接依赖查找
- 通过org.springframework.context.ConfigurableApplicationContext#getEnvironment
依赖注入@Value
依赖注入@Value实现:AutowiredAnnotationBeanPostProcessor
Spring类型转换在Environment中的运用
- 底层实现:org.springframework.core.env.PropertySourcesPropertyResolver
- 核心方法:convertValueIfNecessary(Object,Class)
Spring类型转换在@Value中的运用
- 底层实现:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
- org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
- 底层服务:org.springframework.beans.TypeConverter
- 默认实现:org.springframework.beans.TypeConverterDelegate
- java.beans.PropertyEditor
- org.springframework.core.convert.ConversionService
- 默认实现:org.springframework.beans.TypeConverterDelegate
Spring配置属性源PropertySource
-
API
- 单配置属性源-org.springframework.core.env.PropertySource
- 多配置属性源-org.springframework.core.env.PropertySources
-
注解
- 单配置属性源-@org.springframework.context.annotation.PropertySource
- 多配置属性源-@org.springframework.context.annotation.PropertySources
-
关联
- 存储对象-org.springframework.core.env.MutablePropertySources
- 关联方法-org.springframework.core.env.ConfigurableEnvironment#getPropertySources
Spring内建的配置属性源
PropertySource类型 | 说明 |
---|---|
org.springframework.core.env.CommandLinePropertySource | 命令行配置属性源 |
org.springframework.jndi.JndiPropertySource | JNDI配置属性源 |
org.springframework.core.env.PropertiesPropertySource | Properties配置属性源 |
org.springframework.web.context.support.ServletConfigPropertySource | Servlet配置属性源 |
org.springframework.web.context.support.ServletContextPropertySource | ServletContext配置属性源 |
org.springframework.core.env.SystemEnvironmentPropertySource | 环境变量配置属性源 |
org.springframework.core.env.MapPropertySource | Map配置属性源 |
… | … |
基于注解扩展Spring配置属性源
@org.springframework.context.annotation.PropertySource实现原理
- 入口-org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
- org.springframework.context.annotation.ConfigurationClassParser#processPropertySource
- 4.3新增语义
- 配置属性字符编码-encoding
- org.springframework.core.io.support.PropertySourceFactory
- 适配对象-org.springframework.core.env.CompositePropertySource
基于API扩展Spring配置属性源
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 注册 Configuration Class
context.register(EnvironmentPropertySourceChangeDemo.class);
// 在 Spring 应用上下文启动前,调整 Environment 中的 PropertySource
ConfigurableEnvironment environment = context.getEnvironment();
// 获取 MutablePropertySources 对象
MutablePropertySources propertySources = environment.getPropertySources();
// 动态地插入 PropertySource 到 PropertySources 中
Map<String, Object> source = new HashMap<>();
source.put("user.name", "小马哥");
MapPropertySource propertySource = new MapPropertySource("first-property-source", source);
propertySources.addFirst(propertySource);
// 启动 Spring 应用上下文
context.refresh();
source.put("user.name", "007");
EnvironmentPropertySourceChangeDemo environmentPropertySourceChangeDemo = context.getBean(EnvironmentPropertySourceChangeDemo.class);
System.out.println(environmentPropertySourceChangeDemo.userName);
for (PropertySource ps : propertySources) {
System.out.printf("PropertySource(name=%s) 'user.name' 属性:%s\n", ps.getName(), ps.getProperty("user.name"));
}
// 关闭 Spring 应用上下文
context.close();
}