javascript
Spring Environment
Environment是當前應用運行環境的公開接口,主要包括應用程序運行環境的兩個關鍵方面:配置文件(profiles)和屬性(properties)。
- profiles:
profile配置是一個被命名的、bean定義的邏輯組,這些bean只有在給定的profile配置激活時才會注冊到容器
- properties:
properties屬性可能來源于properties文件、JVM properties、system環境變量、JNDI、servlet context parameters上下文參數、專門的properties對象,Maps等等.
?
類圖
源碼解析
PropertyResolver
PropertyResolver提供屬性訪問功能,并能夠解析占位符屬性(${...})。
public interface PropertyResolver {boolean containsProperty(String key);@NullableString getProperty(String key);String getProperty(String key, String defaultValue);@Nullable<T> T getProperty(String key, Class<T> targetType);<T> T getProperty(String key, Class<T> targetType, T defaultValue);//如果不存在屬性值,則拋出異常String getRequiredProperty(String key) throws IllegalStateException;<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;//解析占位符String resolvePlaceholders(String text);String resolveRequiredPlaceholders(String text) throws IllegalArgumentException; }ConfigurablePropertyResolver
ConfigurablePropertyResolver,繼承自PropertyResolver,主要提供屬性類型轉換(基于org.springframework.core.convert.ConversionService)功能。
public interface ConfigurablePropertyResolver extends PropertyResolver {ConfigurableConversionService getConversionService();void setConversionService(ConfigurableConversionService conversionService);void setPlaceholderPrefix(String placeholderPrefix);void setPlaceholderSuffix(String placeholderSuffix);void setValueSeparator(@Nullable String valueSeparator);void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);void setRequiredProperties(String... requiredProperties);void validateRequiredProperties() throws MissingRequiredPropertiesException; }
Environment
Environment繼承自PropertyResolver,提供訪問和判斷profiles的功能。
public interface Environment extends PropertyResolver {String[] getActiveProfiles();String[] getDefaultProfiles();@Deprecatedboolean acceptsProfiles(String... profiles);//判斷profiles是否被激活boolean acceptsProfiles(Profiles profiles); }ConfigurableEnvironment
ConfigurableEnvironment繼承自ConfigurablePropertyResolver和Environment,提供設置激活的profile和默認的profile的功能以及合并profiles,
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {void setActiveProfiles(String... profiles);void addActiveProfile(String profile);void setDefaultProfiles(String... profiles);MutablePropertySources getPropertySources();//返回System#getProperties()的值,應用了SecurityManagerMap<String, Object> getSystemProperties();//返回System#getenv()的值,應用了SecurityManagerMap<String, Object> getSystemEnvironment();void merge(ConfigurableEnvironment parent);}ConfigurableWebEnvironment
ConfigurableWebEnvironment繼承自ConfigurableEnvironment,并且提供配置Servlet上下文和Servlet參數的功能。
public interface ConfigurableWebEnvironment extends ConfigurableEnvironment {void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig); }ConfigurableReactiveWebEnvironment
專為reactive項目制定的ConfigurableEnvironment
public interface ConfigurableReactiveWebEnvironment extends ConfigurableEnvironment {}AbstractPropertyResolver
AbstractPropertyResolver是ConfigurablePropertyResolver接口的抽象實現類,提供了大部分接口方法的默認實現,將核心的getProperty(String key, Class<T> targetType)方法留給子類實現,resolvePlaceholders(String text)方法則由PropertyPlaceholderHelper提供默認實現。
屬性
//轉換服務@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<>();conversionService
轉換服務是轉換器(Converter)的容器(ConverterRegistry)。
public interface ConverterRegistry {void addConverter(Converter<?, ?> converter);<S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter);void addConverter(GenericConverter converter);void addConverterFactory(ConverterFactory<?, ?> factory);void removeConvertible(Class<?> sourceType, Class<?> targetType); }如果沒有設置,默認使用:DefaultConversionService
解析占位符
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {return helper.replacePlaceholders(text, this::getPropertyAsRawString);}獲取原始值,再替換占位符。?
PropertyPlaceholderHelper
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {Assert.notNull(value, "'value' must not be null");return parseStringValue(value, placeholderResolver, null);}parseStringValue遞歸解析占位符。遇到${開頭就會查找最后一個}符號,將最外層占位符內的內容作為新的value再次傳入 parseStringValue()方法中,這樣最深層次也就是最先返回的就是最里層的占位符名字。調用placeholderResolver將占位符名字轉換成它代表的值。如果值為null,則考慮使用默認值(valueSeparator后的內容)賦值給propVal。由于placeholderResolver轉換過的值有可能還會包含占位符所以在此調用parseStringValue()方法將帶有占位符的propVal傳入返回真正的值,用propVal替換占位符。如果propVal==null,判斷是否允許忽略不能解析的占位符,如果可以重置startIndex繼續解析同一層次的占位符。否則拋出異常。這個函數的返回值就是它上一層次的占位符解析值。
需要注意的一點是:判斷嵌套的占位符是依據simplePrefix。
PropertySourcesPropertyResolver
PropertySourcesPropertyResolver是體系中唯一的完整實現類。它以PropertySources屬性源集合(內部持有屬性源列表List<PropertySource>)為屬性值的來源,按序遍歷每個PropertySource,獲取到一個非null的屬性值則返回。
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {if (this.propertySources != null) {for (PropertySource<?> propertySource : this.propertySources) { ......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);}}}return null;}protected String getPropertyAsRawString(String key) {return getProperty(key, String.class, false);}AbstractEnvironment
屬性
//是否忽略系統環境(System#getenv())的屬性名稱public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore";//激活的profiles的屬性名稱(屬性值以逗號分隔)public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";//激活的profiles的屬性名稱(屬性值以逗號分隔)public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";//默認profile的名稱:defaultprotected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";//激活的profilesprivate final Set<String> activeProfiles = new LinkedHashSet<>();//默認的profilesprivate final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());//propertySourcesprivate final MutablePropertySources propertySources = new MutablePropertySources();private final ConfigurablePropertyResolver propertyResolver =new PropertySourcesPropertyResolver(this.propertySources);由子類設置PropertySource
protected void customizePropertySources(MutablePropertySources propertySources) {}獲取profiles
protected Set<String> doGetActiveProfiles() {synchronized (this.activeProfiles) {if (this.activeProfiles.isEmpty()) {String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME);if (StringUtils.hasText(profiles)) {setActiveProfiles(StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(profiles)));}}return this.activeProfiles;}}
StandardEnvironment
StandardEnvironment繼承自AbstractEnvironment,非Servlet(Web)環境下的標準Environment實現。提供系統屬性以及系統環境變量的獲取。
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";@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()));}}
StandardServletEnvironment
StandardServletEnvironment繼承自StandardEnvironment,Servlet(Web)環境下的標準Environment實現。
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";@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);}}StandardReactiveWebEnvironment
用于reactive web 應用。
public class StandardReactiveWebEnvironment extends StandardEnvironment implements ConfigurableReactiveWebEnvironment {}MutablePropertySources
提供PropertySource的容器。內部存儲為CopyOnWriteArrayList。
PropertySource
PropertySource 類 設計用來存放<key,value>對象的抽象類。同時name,source都是final在初始化后不在變化。
PropertySource的最常用子類是MapPropertySource、PropertiesPropertySource、ResourcePropertySource、StubPropertySource、ComparisonPropertySource:
- MapPropertySource:source指定為Map實例的PropertySource實現。
- PropertiesPropertySource:source指定為Map實例的PropertySource實現,內部的Map實例由Properties實例轉換而來。
- ResourcePropertySource:繼承自PropertiesPropertySource,source指定為通過Resource實例轉化為Properties再轉換為Map實例。
- StubPropertySource:PropertySource的一個內部類,source設置為null,實際上就是空實現。
- ComparisonPropertySource:所有屬性訪問方法強制拋出異常,作用就是一個不可訪問屬性的空實現。
SpringBoot啟動
在Spring boot 啟動時,會準備環境Environment,在Environment準備好之后,會廣播一個ApplicationEnvironmentPreparedEvent:調用各listener的environmentPrepared(ConfigurableEnvironment environment)方法,
listeners.environmentPrepared(environment);void environmentPrepared(ConfigurableEnvironment environment) {for (SpringApplicationRunListener listener : this.listeners) {listener.environmentPrepared(environment);}}@Overridepublic void environmentPrepared(ConfigurableEnvironment environment) {this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));}此時獲取到的事件監聽器包括:ConfigFileApplicationListener 等
public void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationEnvironmentPreparedEvent) {onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);}if (event instanceof ApplicationPreparedEvent) {onApplicationPreparedEvent(event);}}private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();postProcessors.add(this);AnnotationAwareOrderComparator.sort(postProcessors);for (EnvironmentPostProcessor postProcessor : postProcessors) {postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());}}List<EnvironmentPostProcessor> loadPostProcessors() {return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());}在onApplicationEnvironmentPreparedEvent()方法中,會處理EnvironmentPostProcessor,默認4個:
# Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\ org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\ org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor同時ConfigFileApplicationListener 繼承EnvironmentPostProcessor,postProcessEnvironment()實現如下:
@Overridepublic void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {addPropertySources(environment, application.getResourceLoader());}protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {RandomValuePropertySource.addToEnvironment(environment);new Loader(environment, resourceLoader).load();}會使用封裝的Loader加載配置
Loader
Loader類是ConfigFileApplicationListener 的一個內部類,用于加載配置。
屬性
private class Loader {//環境信息private final ConfigurableEnvironment environment;//占位符解析器private final PropertySourcesPlaceholdersResolver placeholdersResolver;//資源加載器//private final ResourceLoader resourceLoader;屬性加載器private final List<PropertySourceLoader> propertySourceLoaders;//profileprivate Deque<Profile> profiles;//處理的profileprivate List<Profile> processedProfiles;private boolean activatedProfiles;//已加載的配置private Map<Profile, MutablePropertySources> loaded;private Map<DocumentsCacheKey, List<Document>> loadDocumentsCache = new HashMap<>(); }初始化
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {this.environment = environment;//構造PlaceholdersResolverthis.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();//從factories中加載類this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,getClass().getClassLoader());} # PropertySource Loaders org.springframework.boot.env.PropertySourceLoader=\ org.springframework.boot.env.PropertiesPropertySourceLoader,\ org.springframework.boot.env.YamlPropertySourceLoader?
使用
Properties
properties配置文件
<context:property-placeholder location="classpath:db.properties" />注解方式
@Configuration @PropertySource("classpath:db.properties") public class TestProperties {@AutowiredEnvironment env;public void getProperty() {environment.getProperty("jdbc.driverClassName");} }Profiles
配置
<beans profile="test"><context:property-placeholder location="/WEB-INF/test.properties" /> </beans><beans profile="beta"><context:property-placeholder location="/WEB-INF/beta.properties" /> </beans>激活
- 代碼指定
- 配置文件指定
- 啟動參數指定
JAVA_OPTS="-Dspring.profiles.active=test"
Enviorment
引用
- Autoware
- 實現EnvironmentAware接口
?
?
?
?
總結
以上是生活随笔為你收集整理的Spring Environment的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring BeanDefinitio
- 下一篇: Spring @Conditional