本文主要是介绍Spring Boot v2.4.4源码解析(二)运行时环境Environment,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Spring Boot当前应用运行环境Environment
一、引入
在Spring Boot启动函数SpringApplication#run(String... args)
中会调用prepareEnvironment(listeners, bootstrapContext, applicationArguments)
函数准备运行时环境。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {// 创建或者配置环境ConfigurableEnvironment environment = getOrCreateEnvironment();configureEnvironment(environment, applicationArguments.getSourceArgs());ConfigurationPropertySources.attach(environment);listeners.environmentPrepared(bootstrapContext, environment);DefaultPropertiesPropertySource.moveToEnd(environment);configureAdditionalProfiles(environment);bindToSpringApplication(environment);if (!this.isCustomEnvironment) {environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());}ConfigurationPropertySources.attach(environment);return environment;
}
该方法首先会根据getOrCreateEnvironment()
方法获取ConfigurableEnvironment;
private ConfigurableEnvironment getOrCreateEnvironment() {if (this.environment != null) {return this.environment;}switch (this.webApplicationType) {case SERVLET:return new StandardServletEnvironment();case REACTIVE:return new StandardReactiveWebEnvironment();default:return new StandardEnvironment();}
}
第一次进入该方法environment
为空,需要根据webApplicationType
当前应用类型创建,应用类型主要分为三种:
- SERVLET 普通web应用;
- REACTIVE 响应式web应用;
- 其他标准引用
SpringApplication
会在构造方法中调用 WebApplicationType#deduceFromClasspath()
推断出应用类型并初始化webApplicationType
,具体源码参数WebApplicationType
。笔者当前环境为普通web应用,所以最后将会创建StandardServletEnvironment。
二、继承关系
看下StandardServletEnvironment
类UML类图:
三、属性解析
1. 属性解析顶级接口PropertyResolver
类图中顶级接口为PropertyResolver
, 该接口定义了一些方法用于针对任何基础源解析属性。
public interface PropertyResolver {// 判断给定的属性键是否可解析, 或者说判断给定key解释出的value是否不为nullboolean containsProperty(String key);// 返回给定key所对应的属性值, 如果给定的key不能解析则返回null@NullableString getProperty(String key);// 返回给定key所对应的属性值, 如果给定的key不能解析则返回defaultValueString getProperty(String key, String defaultValue);// 解析给定key所对应的属性值为目标类型targetType, 如果给定的key不能解析则返回null@Nullable<T> T getProperty(String key, Class<T> targetType);// 解析给定key所对应的属性值, 并将该值转换成目标类型targetType, 如果给定的key不能解析则返回defaultValue<T> T getProperty(String key, Class<T> targetType, T defaultValue);// 返回给定key所对应的属性值, 如果给定的key不能解析则抛IllegalStateException异常String getRequiredProperty(String key) throws IllegalStateException;// 解析给定key所对应的属性值, 并将该值转换成目标类型targetType(非null), 如果给定的key不能解析则抛IllegalStateException异常<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;// 解析给定文本中${...}形式的占位符, 并将解析出占位符名称作为key用getProperty()函数获取属性值并将其替换// 对于不能获得占位符名称属性值且没有默认值的占位符将会被忽略并保持不变,String resolvePlaceholders(String text);// 解析给定文本中${...}形式的占位符, 并将解析出占位符名称作为key用getProperty()函数获取属性值并将其替换// 对于不能获得占位符名称属性值且没有默认值的占位符将会抛出IllegalArgumentException 异常String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
PropertyResolver
继承链主要有两个分支:
PropertyResolver
->ConfigurablePropertyResolver
->AbstractPropertyResolver
->PropertySourcesPropertyResolver
分支;PropertyResolver
->Environment
->ConfigurableEnvironment
->AbstractEnvironment
->XXXEnvironment
分支;
真正对PropertyResolver
接口功能实现是在第一条分支,第二条分支会在AbstractEnvironment
内部组合一个PropertySourcesPropertyResolver
实例,并将PropertyResolver
接口方法实现代理给PropertySourcesPropertyResolver
;
2. ConfigurablePropertyResolver
看下PropertyResolver
的一个子接口 ConfigurablePropertyResolver
, 顾名思义,该接口是可配置的PropertyResolver
。
ConfigurableXXX
成了Spring的一种命名规范,或者说是一种设计模式, 表示裸配置;Spring很多接口都是读写分离的,最顶层接口一般都只会提供只读方法,这是Spring框架设计的一般规律之一。
PropertyResolver
接口中有些方法需要将属性值装换成目标类型targetType
, 类型转换功能由ConversionService
接口提供, 所以ConfigurablePropertyResolver
最重要就是定义访问和定制ConversionService
方法。除此之外, ConfigurablePropertyResolver
扩展定义必要属性校验、占位符前缀、占位符后缀、占位符分隔符等一些列的功能:
public interface ConfigurablePropertyResolver extends PropertyResolver {// 返回对属性值执行类型转换时使用的ConfigurableConversionService// 由于返回的ConversionService具有可配置特性, 所以可以很方便地添加和删除单个Converter// 例如:// ConfigurableConversionService cs = env.getConversionService();// cs.addConverter(new FooConverter());ConfigurableConversionService getConversionService();// 设置用于执行属性值类型转换的ConfigurableConversionService// 注意, 与其重新设置ConversionService, 一种更优雅的方式是通过getConversionService()获取ConfigurableConversionService, 并通过像addConverter等方法删除或者添加单个Convertervoid setConversionService(ConfigurableConversionService conversionService);// 设置占位符前缀void setPlaceholderPrefix(String placeholderPrefix);// 设置占位符后缀void setPlaceholderSuffix(String placeholderSuffix);// 设置占位符与默认值分隔符void setValueSeparator(@Nullable String valueSeparator);// 设置在遇到解析属性值中不能解析的占位符时, 是或否抛异常// 即该方法确定占位符江西是否是严格模式;// 传参为false时, 严格模式, 属性值中遇到不能解析的占位符将会抛异常// 传参为true时, 非严格模式, 属性值中不能解析的占位符将保持原有${...}形式// 在调用getProperty(String)及其重载方法获取属性值且属性值包含不能解析成功的占位符时, 将会其获取该值void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);// 指定必须存在的属性void setRequiredProperties(String... requiredProperties);// 验证setRequiredProperties指定的每个属性是否存在并能否解析为null值。// 如果遇到任何必须存在的属性不能解析时抛MissingRequiredPropertiesException异常void validateRequiredProperties() throws MissingRequiredPropertiesException;
}
3. AbstractPropertyResolver
AbstractPropertyResolver
实现了ConfigurablePropertyResolver
除getProperty(String key, Class<T> targetType)
之外的所有方法,并且只提供一个抽象方法getPropertyAsRawString(String key)
给子类去实现。
public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {// volatile 保证double check实现单例线程可见以及防止指令重排序@Nullableprivate volatile ConfigurableConversionService conversionService;// 非严格模式占位符解析器@Nullableprivate PropertyPlaceholderHelper nonStrictHelper;// 严格模式占位符解析器@Nullableprivate PropertyPlaceholderHelper strictHelper;// 占位符解析默认严格模式private boolean ignoreUnresolvableNestedPlaceholders = false;// 默认${private String placeholderPrefix = SystemPropertyUtils.PLACEHOLDER_PREFIX;// 默认是}private String placeholderSuffix = SystemPropertyUtils.PLACEHOLDER_SUFFIX;// 默认是:@Nullableprivate String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR;// 必要属性private final Set<String> requiredProperties = new LinkedHashSet<>();// double check保证单例@Overridepublic ConfigurableConversionService getConversionService() {ConfigurableConversionService cs = this.conversionService;if (cs == null) {synchronized (this) {cs = this.conversionService;if (cs == null) {cs = new DefaultConversionService();this.conversionService = cs;}}}return cs;}... // 省略get/set@Overridepublic void setRequiredProperties(String... requiredProperties) {Collections.addAll(this.requiredProperties, requiredProperties);}@Overridepublic void validateRequiredProperties() {MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();for (String key : this.requiredProperties) {if (this.getProperty(key) == null) {ex.addMissingRequiredProperty(key);}}if (!ex.getMissingRequiredProperties().isEmpty()) {throw ex;}}// 所有getProperty()重载方法, getRequiredProperty()重载方法及containsProperty()方法// 最终都会调用getProperty(String key, Class<T> targetType)方法, 这里省略@Overridepublic String resolvePlaceholders(String text) {if (this.nonStrictHelper == null) {this.nonStrictHelper = createPlaceholderHelper(true);}return doResolvePlaceholders(text, this.nonStrictHelper);}@Overridepublic String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {if (this.strictHelper == null) {this.strictHelper = createPlaceholderHelper(false);}return doResolvePlaceholders(text, this.strictHelper);}// 解析给定字符串中的占位符// ignoreUnresolvableNestedPlaceholders确定无法解析的占位符是否应该引发异常或被忽略// 调用getProperty及其重载方法会隐式解析属性值中嵌套的占位符// 相比之下, resolvePlaceholders resolveRequiredPlaceholders不会委托给这个方法// 而是执行它们自己指定的对无法解析的占位符的处理protected String resolveNestedPlaceholders(String value) {if (value.isEmpty()) {return value;}return (this.ignoreUnresolvableNestedPlaceholders ?resolvePlaceholders(value) : resolveRequiredPlaceholders(value));}private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,this.valueSeparator, ignoreUnresolvablePlaceholders);}private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {return helper.replacePlaceholders(text, this::getPropertyAsRawString);}// 类型转换@SuppressWarnings("unchecked")@Nullableprotected <T> T convertValueIfNecessary(Object value, @Nullable Class<T> targetType) {if (targetType == null) {return (T) value;}ConversionService conversionServiceToUse = this.conversionService;if (conversionServiceToUse == null) {if (ClassUtils.isAssignableValue(targetType, value)) {return (T) value;}conversionServiceToUse = DefaultConversionService.getSharedInstance();}return conversionServiceToUse.convert(value, targetType);}// 以原始字符串的形式检索指定的属性, 不会解析嵌套占位符@Nullableprotected abstract String getPropertyAsRawString(String key);
从上面源码可以看出,AbstractPropertyResolver
内部定义了两个PropertyPlaceholderHelper
,占位符
strictHelper
严格模式下占位符解析器;nonStrictHelper
非严格模式解析器;
处理占位的核心逻辑在PropertyPlaceholderHelper
身上,placeholderResolver
lambda表达式即为方法getPropertyAsRawString(String key)
。该类不熟悉的可以参考Spring Boot v2.4.4源码分析(一)字符串占位符解析器 PropertyPlaceholderHelper 这篇博文。
另外该类还提供了方法 convertValueIfNecessary(Object value, @Nullable Class<T> targetType)
实现对象类型转换。
4. PropertySourcesPropertyResolver
AbstractPropertyResolver
只有一个实现类PropertySourcesPropertyResolver
,主要是负责提供数据源。
public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {// 包含一个或多个PropertySource对象的持有者@Nullableprivate final PropertySources propertySources;// 唯一构造函数, 必须制定数据源public PropertySourcesPropertyResolver(@Nullable PropertySources propertySources) {this.propertySources = propertySources;}@Overridepublic boolean containsProperty(String key) {if (this.propertySources != null) {// 挨个判断每个数据源是否包含key属性for (PropertySource<?> propertySource : this.propertySources) {if (propertySource.containsProperty(key)) {return true;}}}return false;}// getProperty(String key), getProperty(String key, Class<T> targetValueType)// 以及getPropertyAsRawString(String key) 都会调用// getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders)方法// 这里省略这几个方法@Nullableprotected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {if (this.propertySources != null) {// 从数据源中获取属性值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;}protected void logKeyFound(String key, PropertySource<?> propertySource, Object value) {if (logger.isDebugEnabled()) {logger.debug("Found key '" + key + "' in PropertySource '" + propertySource.getName() +"' with value of type " + value.getClass().getSimpleName());}}
四、环境
接下来看PropertyResolver
另外一条分支,也是比较重要分支;
1. Environment
首先,Environment
接口直接继承PropertyResolver
,该接口代表了当前应用运行的环境。Environment
抽象出应用环境两个关键概念:
- profiles, 一个
profile
是对bean
的一个逻辑命名分组,只有该分组在当前环境下处于激活状态,该分组中的bean
才会被注册到IOC容器中。常用的profile
有dev
、test
、prod
等。可以使用XML定义bean
时或者通过@Profile
注解方式为bean
指定profile
。Environment
通过getActiveProfiles()
方法决定哪些profile
处于激活状态,通过getDefaultProfiles()
方法指定哪些profile
默认激活。 - properties,与属性相关的方法通过父接口
PropertyResolver
向外暴露。属性在几乎所有应用中扮演着重要角色,环境中的属性可能源自配置文件、JVM系统属性、系统环境变量、JNDI等等。Environment
为用户提供了更便捷的方式去配置属性源和解析属性值。
被ApplicationContext
管理的bean
可以通过实现接口EnvironmentAware
获取应用Environment
,如
@Component
public class FooBar implements EnvironmentAware {private Environment environment;@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}}
或者直接将Environment
直接注入到该bean
中。
看下Environment
源码:
public interface Environment extends PropertyResolver {// 返回当前环境激活的profile// 可以通过设置spring.profiles.active系统属性或者调用ConfigurableEnvironment#setActiveProfiles(String...)激活profile// 如果没有明确指定激活的profile, 将会自动激活getDefaultProfiles()方法返回的默认profileString[] getActiveProfiles();// 默认profileString[] getDefaultProfiles();// 判断给定的profiles是否有一个或者多个处于激活状态// 如果没有明确指定激活的profile, 则此方法相当于判断默profile是否包含一个或者多个给定的profiles// 如果参数可以以'!'开头, 表示逻辑取反// 例如, 如果profile 'p1'处于激活状态或者'p2'处于非激活状态, acceptsProfiles("p1", "!p2")将返回true@Deprecatedboolean acceptsProfiles(String... profiles);// 判断getActiveProfiles()返回的profile是否与给定的Profiles匹配。boolean acceptsProfiles(Profiles profiles);
}
2. ConfigurableEnvironment
Environment
也提供可配置接口ConfigurableEnvironment
,该接口需要完成两件事:定义配置profile方法和配置property方法,而配置property方法已经在ConfigurablePropertyResolver
中定义,直接继承过来即可,所以只需定义配置property方法。
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {// 指定当前环境激活profile集;// 该方法会使用给定的参数将原有已经存在激活profile完全替换;// 如果传空参数, 相当于清空已经激活的profile集;// 可以使用addActiveProfile()方法保留原有激活profile, 并添加激活profile;void setActiveProfiles(String... profiles);void addActiveProfile(String profile);// 指定默认profile集, 如果没有通过显式指定激活的profile,那么改方法指定的profile集将会被激活void setDefaultProfiles(String... profiles);// 返回当前环境属性源集合PropertySources, 属性解析值时用遍历该PropertySources获取属性值;// 得到PropertySources后, 就可以操作属性源;// 例如, MutablePropertySources提供的addFirst(), addFirst(), addBefore()// addAfter()可以细粒度地控制各属性源之间的顺序;// 这有助于确保某些用户自定义的属性源的搜索优先于默认属性源;MutablePropertySources getPropertySources();Map<String, Object> getSystemProperties();Map<String, Object> getSystemEnvironment();// 将给定的父Environment的活动激活profile, 默认profile和属性源添加到当前Environment的各自集合中;// 对于同时存在于父子Environment中的同名属性源, 子Environment的将被保留,父实例Environment的将会被丢弃;// 激活profile, 默认profile亦是如此;// 在任何情况下, 父Environment都保持不变, // 注意, 在调用merge()之后发生的对父Environment的任何更改都不会反映在子Environment中void merge(ConfigurableEnvironment parent);
3. AbstractEnvironment
AbstractEnvironment
对接口ConfigurableEnvironment
所有方法提供抽象实现。
public abstract class AbstractEnvironment implements ConfigurableEnvironment {// 系统属性名, 确定Spring是否忽略系统环境变量public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore";// 系统属性名, 指定当前环境激活profilepublic static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";// 系统属性名, 指定当前环境默认profilepublic static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";// 默认profile默认值 protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";// 激活profileprivate final Set<String> activeProfiles = new LinkedHashSet<>();// 默认profileprivate final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());// 属性源集合private final MutablePropertySources propertySources;// 属性解析静态代理类, AbstractEnvironment 属性解析相关功能由propertyResolver实现private final ConfigurablePropertyResolver propertyResolver;public AbstractEnvironment() {this(new MutablePropertySources());}// 实际的构造函数, 需要传数据源集合protected AbstractEnvironment(MutablePropertySources propertySources) {this.propertySources = propertySources;this.propertyResolver = createPropertyResolver(propertySources);// 定制化propertySourcescustomizePropertySources(propertySources);}// ConfigurablePropertyResolver 工厂方法protected ConfigurablePropertyResolver createPropertyResolver(MutablePropertySources propertySources) {return new PropertySourcesPropertyResolver(propertySources);}// 此处省略ConfigurablePropertyResolver get方法// 重要, 重要, 重要, 定制propertySources, 子类主要重写该方法protected void customizePropertySources(MutablePropertySources propertySources) {}// 默认profile默认值protected Set<String> getReservedDefaultProfiles() {return Collections.singleton(RESERVED_DEFAULT_PROFILE_NAME);}@Overridepublic String[] getActiveProfiles() {return StringUtils.toStringArray(doGetActiveProfiles());}// 返回通过setActiveProfiles()显示设置activeProfiles的值// 如果该值为空, 则返回属性spring.profiles.active的解析值protected Set<String> doGetActiveProfiles() {synchronized (this.activeProfiles) {if (this.activeProfiles.isEmpty()) {// 解析属性spring.profiles.active, 看用户是否通过系统属性或者配置文件等方式设置该值String profiles = doGetActiveProfilesProperty();if (StringUtils.hasText(profiles)) {// 解析完成后设置值setActiveProfiles(StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(profiles)));}}return this.activeProfiles;}}@Nullableprotected String doGetActiveProfilesProperty() {return getProperty(ACTIVE_PROFILES_PROPERTY_NAME);}@Overridepublic void setActiveProfiles(String... profiles) {Assert.notNull(profiles, "Profile array must not be null");if (logger.isDebugEnabled()) {logger.debug("Activating profiles " + Arrays.asList(profiles));}synchronized (this.activeProfiles) {// 如ConfigurableEnvironment定义, 先清空原有值, 再将给该参数添加到activeProfilesthis.activeProfiles.clear();for (String profile : profiles) {validateProfile(profile);this.activeProfiles.add(profile);}}}@Overridepublic void addActiveProfile(String profile) {if (logger.isDebugEnabled()) {logger.debug("Activating profile '" + profile + "'");}validateProfile(profile);doGetActiveProfiles();synchronized (this.activeProfiles) {this.activeProfiles.add(profile);}}@Overridepublic String[] getDefaultProfiles() {return StringUtils.toStringArray(doGetDefaultProfiles());}protected Set<String> doGetDefaultProfiles() {synchronized (this.defaultProfiles) {// 如果是默认值, 解析spring.profiles.default, 看用户是否通过系统属性或者配置文件等方式设置该值if (this.defaultProfiles.equals(getReservedDefaultProfiles())) {String profiles = doGetDefaultProfilesProperty();if (StringUtils.hasText(profiles)) {setDefaultProfiles(StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(profiles)));}}return this.defaultProfiles;}}@Overridepublic void setDefaultProfiles(String... profiles) {Assert.notNull(profiles, "Profile array must not be null");synchronized (this.defaultProfiles) {this.defaultProfiles.clear();for (String profile : profiles) {validateProfile(profile);this.defaultProfiles.add(profile);}}}@Override@Deprecatedpublic boolean acceptsProfiles(String... profiles) {Assert.notEmpty(profiles, "Must specify at least one profile");for (String profile : profiles) {if (StringUtils.hasLength(profile) && profile.charAt(0) == '!') {// 取反时, 判断该profile是否非激活if (!isProfileActive(profile.substring(1))) {return true;}}// 判断该profile是否激活else if (isProfileActive(profile)) {return true;}}return false;}@Overridepublic boolean acceptsProfiles(Profiles profiles) {Assert.notNull(profiles, "Profiles must not be null");return profiles.matches(this::isProfileActive);}protected boolean isProfileActive(String profile) {validateProfile(profile);Set<String> currentActiveProfiles = doGetActiveProfiles();// activeProfiles不空时, 判断activeProfiles是否包含// activeProfiles为空时, 判断defaultProfiles是否包含return (currentActiveProfiles.contains(profile) ||(currentActiveProfiles.isEmpty() && doGetDefaultProfiles().contains(profile)));}protected void validateProfile(String profile) {if (!StringUtils.hasText(profile)) {throw new IllegalArgumentException("Invalid profile [" + profile + "]: must contain text");}if (profile.charAt(0) == '!') {throw new IllegalArgumentException("Invalid profile [" + profile + "]: must not begin with ! operator");}}@Overridepublic MutablePropertySources getPropertySources() {return this.propertySources;}// 如果当前SecurityManager允许, 则返回System#getProperties()的值,// 否则返回一个map实现类, 对该map所有key的访问都将调用System#getProperty(String),// 参考AbstractEnvironment#getSystemProperties()方法;// 注意, 大部分Environment实现类, 会默认将系统属性map作为属性源添加到PropertySources中;// 因此, 建议不要直接使用此方法, 除非明确打算绕过其他属性源;@Override@SuppressWarnings({"rawtypes", "unchecked"})public Map<String, Object> getSystemProperties() {try {return (Map) System.getProperties();}catch (AccessControlException ex) {return (Map) new ReadOnlySystemAttributesMap() {@Override@Nullableprotected String getSystemAttribute(String attributeName) {try {return System.getProperty(attributeName);}catch (AccessControlException ex) {if (logger.isInfoEnabled()) {logger.info("Caught AccessControlException when accessing system property '" +attributeName + "'; its value will be returned [null]. Reason: " + ex.getMessage());}return null;}}};}}// 同getSystemProperties(), 只不过调用的是System的getenv()和getenv(String)方法@Override@SuppressWarnings({"rawtypes", "unchecked"})public Map<String, Object> getSystemEnvironment() {if (suppressGetenvAccess()) {return Collections.emptyMap();}try {return (Map) System.getenv();}catch (AccessControlException ex) {return (Map) new ReadOnlySystemAttributesMap() {@Override@Nullableprotected String getSystemAttribute(String attributeName) {try {return System.getenv(attributeName);}catch (AccessControlException ex) {if (logger.isInfoEnabled()) {logger.info("Caught AccessControlException when accessing system environment variable '" +attributeName + "'; its value will be returned [null]. Reason: " + ex.getMessage());}return null;}}};}}// 判断是否能访问 System#getenv()/System#getenv(String)方法protected boolean suppressGetenvAccess() {return SpringProperties.getFlag(IGNORE_GETENV_PROPERTY_NAME);}@Overridepublic void merge(ConfigurableEnvironment parent) {for (PropertySource<?> ps : parent.getPropertySources()) {if (!this.propertySources.contains(ps.getName())) {this.propertySources.addLast(ps);}}String[] parentActiveProfiles = parent.getActiveProfiles();if (!ObjectUtils.isEmpty(parentActiveProfiles)) {synchronized (this.activeProfiles) {Collections.addAll(this.activeProfiles, parentActiveProfiles);}}String[] parentDefaultProfiles = parent.getDefaultProfiles();if (!ObjectUtils.isEmpty(parentDefaultProfiles)) {synchronized (this.defaultProfiles) {this.defaultProfiles.remove(RESERVED_DEFAULT_PROFILE_NAME);Collections.addAll(this.defaultProfiles, parentDefaultProfiles);}}}// ConfigurablePropertyResolver接口实现方法由propertyResolver代理, 这里省略}
AbstractEnvironment
留给子类的活就不多了, 只需要定制化属性源就可以了。
PropertySourcesPropertyResolver
在构造时会以propertySources
作为属性源, 而通过上面的源码可以知道,PropertySourcesPropertyResolver
在解析属性值时,会按照属性源顺序依次解析,前面属性源能解析成功就直接返回,即前面属性源具有较高优先级。所以在添加或者删除属性源时顺序就显得格外重要了。
通常会使用两种操作影响数据源顺序:
- 添加数据源时调用的是
addFirst()
,addFirst()
,addBefore()
,addAfter()
哪个方法;
public class FooEnvironment extends AbstractEnvironment{protected FooEnvironment(MutablePropertySources propertySources) {super(propertySources);}public static void main(String[] args) {MutablePropertySources mps = new MutablePropertySources();Map<String, Object> map1 = Maps.newHashMap();map1.put("f", "foo");MapPropertySource mapPs1 = new MapPropertySource("mapPs1", map1);mps.addLast(mapPs1);Map<String, Object> map2 = Maps.newHashMap();map2.put("f", "bar");MapPropertySource mapPs2 = new MapPropertySource("mapPs2", map1);mps.addLast(mapPs2);Environment env = new FooEnvironment(mps);// 输出fooSystem.err.println(env.resolvePlaceholders("${f}"));}
}
由于map1
比map2
先添加到MutablePropertySources
,所以解析占位符时优先使用map1
中的属性。
- 继承
AbstractEnvironment
构造函数肯定会调用到AbstractEnvironment(MutablePropertySources)
,该函数内部会调用customizePropertySources(MutablePropertySources)
,所以在重写该方法时,是先调用父类方法,还是先实现自己定制化逻辑也会影响数据源顺序;
public class Level1Environment extends AbstractEnvironment {@Overrideprotected void customizePropertySources(MutablePropertySources propertySources) {super.customizePropertySources(propertySources); // no-op from base classpropertySources.addLast(new PropertySourceA(...));propertySources.addLast(new PropertySourceB(...));}}public class Level2Environment extends Level1Environment {@Overrideprotected void customizePropertySources(MutablePropertySources propertySources) {super.customizePropertySources(propertySources); // add all from superclasspropertySources.addLast(new PropertySourceC(...));propertySources.addLast(new PropertySourceD(...));}}
如上,Level2Environment
是 Level1Environment
的子类,Level2Environment
先调用父类的属性源定制化函数,再实现自己的定制化操作,那么最终属性源顺序将是PropertySourceA
,PropertySourceB
,PropertySourceC
,PropertySourceD
。
4. StandardEnvironment
StandardEnvironment
类只重写了customizePropertySources(MutablePropertySources)
方法,将系统变量和系统环境变量加到属性源中。
public class StandardEnvironment extends AbstractEnvironment {/** System environment property source name: {@value}. */public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";/** JVM system properties property source name: {@value}. */public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";public StandardEnvironment() {}protected StandardEnvironment(MutablePropertySources propertySources) {super(propertySources);}@Overrideprotected void customizePropertySources(MutablePropertySources propertySources) {propertySources.addLast(new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));}
系统变量中主要包含
- JRE相关的系统属性:
java.home
,java.library.path
,java.class.path
,java.version
,java.runtime.version
等; - 文件相关的系统属性:
file.separator
,path.separator
,line.separator
等; - 用户相关的系统属性:
user.name
,user.home
,user.dir
等; - OS相关的系统属性:
os.name
,os.version
,os.arch
等;
通过-D
设置的VM参数也会加入到系统属性中。
@Component
public class FooBar implements EnvironmentAware, CommandLineRunner{private Environment env;@Overridepublic void setEnvironment(Environment environment) {this.env = environment;}@Overridepublic void run(String... args) throws Exception {// 1.8.0_131System.err.println(env.resolvePlaceholders("${java.version}"));// 6.1System.err.println(env.resolvePlaceholders("${os.version}"));}
}
environment property
是系统级的环境变量,系统当中所有的进程都可以访问到。
一般StandardEnvironment
的继承类重写 customizePropertySources(MutablePropertySources)
时,会先调用StandardEnvironment
的customizePropertySources(MutablePropertySources)
,然后再实现自己定制化逻辑,所以通常系统变量和系统环境变量具有比较高优先级,-D
VM参数优先级比配置文件优先级高。
5. StandardServletEnvironment
最后看下StandardServletEnvironment
public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {/** Servlet context init parameters property source name: {@value}. */public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";/** Servlet config init parameters property source name: {@value}. */public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";/** JNDI property source name: {@value}. */public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";public StandardServletEnvironment() {}protected StandardServletEnvironment(MutablePropertySources propertySources) {super(propertySources);}@Overrideprotected void customizePropertySources(MutablePropertySources propertySources) {propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));}super.customizePropertySources(propertySources);}@Overridepublic void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);}
StandardServletEnvironment
定制化属性源时添加了Servlet
上下文初始化参数和Servlet
配置初始化参数,且优先级比父类StandardEnvironment
系统变量和系统环境变量优先级高。
这篇关于Spring Boot v2.4.4源码解析(二)运行时环境Environment的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!