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);
	@Nullable
	String 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();
	@Deprecated
	boolean 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()的值,應用了SecurityManager
	Map<String, Object> getSystemProperties();
    //返回System#getenv()的值,應用了SecurityManager
	Map<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提供默認實現。

屬性

    //轉換服務
	@Nullable
	private volatile ConfigurableConversionService conversionService;
	@Nullable
	private PropertyPlaceholderHelper nonStrictHelper;
	@Nullable
	private PropertyPlaceholderHelper strictHelper;
	private boolean ignoreUnresolvableNestedPlaceholders = false;
    //佔位符前綴,默認:"${"
	private String placeholderPrefix = SystemPropertyUtils.PLACEHOLDER_PREFIX;
    //佔位符後綴,默認:"}"
	private String placeholderSuffix = SystemPropertyUtils.PLACEHOLDER_SUFFIX;
    //默認值分隔符,默認:":"
	@Nullable
	private 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的名稱:default
	protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";
    //激活的profiles
	private final Set<String> activeProfiles = new LinkedHashSet<>();
    //默認的profiles
	private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());
    //propertySources
	private 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";

	@Override
	protected 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";

	@Override
	protected 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);
	}

	@Override
	public 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:所有屬性訪問方法強制拋出異常,作用就是一個不可訪問屬性的空實現。
public abstract class PropertySource<T> {
	protected final String name;
	protected final T source;
	public boolean equals(@Nullable Object other) {
		return (this == other || (other instanceof PropertySource &&
				ObjectUtils.nullSafeEquals(this.name, ((PropertySource<?>) other).name)));
	}
	public int hashCode() {
		return ObjectUtils.nullSafeHashCode(this.name);
	}
}  

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);
		}
	}


	@Override
	public 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 繼承EnvironmentPostProcessorpostProcessEnvironment()實現如下:

	@Override
	public 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;
        //profile
		private Deque<Profile> profiles;
        //處理的profile
		private 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;
            //構造PlaceholdersResolver
			this.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 {
	@Autowired
	Environment 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>

激活

  • 代碼指定
ConfigurableEnvironment.setActiveProfiles("test")
  • 配置文件指定
<init-param>
  <param-name>spring.profiles.active</param-name>
  <param-value>beta</param-value>
</init-param>
  • 啓動參數指定

JAVA_OPTS="-Dspring.profiles.active=test"

Enviorment

引用

  • Autoware
@Autowired
private Environment environment;
  • 實現EnvironmentAware接口

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章