【源码】Spring —— Environment 解读

前言

Environment
Spring 对整个 环境 的抽象,其实我们很少直接使用它,但是经常借助类似 ${}占位符 去获取一些 系统级别JVM级别 的变量。

Environment 实现了 PropertyResolver 接口,PropertyResolver 提供了大量属性操作相关的方法。

Environment 下有两大“模型”,分别是 profilesproperties

  • profiles: 是对容器配置的一种 逻辑分组 抽象
  • properties:提供了配置层配置文件系统参数JVM参数 等抽象

在容器中,我们可以直接注入 Environment 或者以实现 EnvironmentAware 接口的形式获取 Environment 实例

PropertyResolver

public interface PropertyResolver {

	// 指定 key 是否可用
	boolean containsProperty(String key);

	@Nullable
	// 返回指定 key 对应的值,无法解析则为 null
	String getProperty(String key);

	// 返回指定 key 对应的值,无法解析则返回默认值 defaultValue
	String getProperty(String key, String defaultValue);

	@Nullable
	// 将指定 key 的值转换成 T 类型返回,无法解析则为 null
	<T> T getProperty(String key, Class<T> targetType);

	// 将指定 key 的值转换成 T 类型返回,无法解析则返回默认值 defaultValue
	<T> T getProperty(String key, Class<T> targetType, T defaultValue);

	// 返回指定 key 的值,否则抛出异常 IllegalStateException
	String getRequiredProperty(String key) throws IllegalStateException;

	// 将指定 key 的值转换成 T 类型返回,否则抛出异常 IllegalStateException
	<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;

	// 借助 getProperty 解析 ${...} 占位符
	// 解析失败且没有默认值则原样返回
	String resolvePlaceholders(String text);

	// 同上,解析失败抛出 IllegalArgumentException 异常
	String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;

}

PropertyResolver 接口,定义了一些 属性 的读取、解析等方法

Environment

public interface Environment extends PropertyResolver {

	// 返回所有 激活 的 Profiles
	String[] getActiveProfiles();

	// 返回所有 默认 的 Profiles
	String[] getDefaultProfiles();

	@Deprecated
	// Deprecated 方法,用下面的方法代替
	boolean acceptsProfiles(String... profiles);

	// 匹配给定 profiles 是否激活
	boolean acceptsProfiles(Profiles profiles);

}

主要定义了对 profiles 的访问

Environment
结构并不是很复杂

ConfigurableEnvironment

public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {

	// 设置 active profiles
	void setActiveProfiles(String... profiles);

	// 新增 active profiles
	void addActiveProfile(String profile);

	// 设置 default profiles
	void setDefaultProfiles(String... profiles);

	// 获取一个可操作的 PropertySource 集合,用于自定义属性
	MutablePropertySources getPropertySources();

	// 获取系统级别的属性,相当于 System#getProperties()
	Map<String, Object> getSystemProperties();

	// System#getenv()
	Map<String, Object> getSystemEnvironment();

	// 合并父环境的 active profiles, default profiles, property sources
	// 对于有冲突的以子环境的优先
	void merge(ConfigurableEnvironment parent);

}

拓展 Environment 接口,提供了对 default profilesactive profilesproperty sources 等的操作。其对 property sources 的操作基于 getPropertySources 方法获取的 MutablePropertySources 实现

MutablePropertySourcesPropertySources 的唯一实现类,其内部维护了一个 propertySourceList 用于存储各种各样的 PropertySource ,而且提供了大量的方法用于操作 PropertySource

例如

	// 迭代器获取
	public Iterator<PropertySource<?>> iterator() {
		return this.propertySourceList.iterator();
	}

	// 判断是否存在
	public boolean contains(String name) {
		for (PropertySource<?> propertySource : this.propertySourceList) {
			if (propertySource.getName().equals(name)) {
				return true;
			}
		}
		return false;
	}

	// 根据 name 获取对应 PropertySource
	public PropertySource<?> get(String name) {
		for (PropertySource<?> propertySource : this.propertySourceList) {
			if (propertySource.getName().equals(name)) {
				return propertySource;
			}
		}
		return null;
	}

	// 链表头插入 PropertySource
	public void addFirst(PropertySource<?> propertySource) {
		synchronized (this.propertySourceList) {
			removeIfPresent(propertySource);
			this.propertySourceList.add(0, propertySource);
		}
	}

	// 链尾插入 PropertySource
	public void addLast(PropertySource<?> propertySource) {
		synchronized (this.propertySourceList) {
			removeIfPresent(propertySource);
			this.propertySourceList.add(propertySource);
		}
	}

	// 等等
需要了解 PropertySource 的同学可以阅读下面这个文章

【源码】Spring —— PropertySource 解读

AbstractEnvironment

该类是 Environment 的核心抽象类,基本实现了 profilesproperty 所有相关方法,并预留出一个 钩子方法 供子类实现,我们分步阅读

属性、构造方法
	public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore";

	public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";

	public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";

	protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";

	private final Set<String> activeProfiles = new LinkedHashSet<>();

	private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());

	private final MutablePropertySources propertySources = new MutablePropertySources();

	private final ConfigurablePropertyResolver propertyResolver =
			new PropertySourcesPropertyResolver(this.propertySources);

	public AbstractEnvironment() {
		customizePropertySources(this.propertySources);
	}

	protected void customizePropertySources(MutablePropertySources propertySources) {
	}

定义了一些 属性 名的默认值,定义了几个相关的 缓存 集合和 ConfigurablePropertyResolver 。重要的是在构造方法中调用了 钩子方法 customizePropertySources ,由子类自行复写

profiles 相关方法(部分)
	@Override
	public String[] getActiveProfiles() {
		return StringUtils.toStringArray(doGetActiveProfiles());
	}

	// 解析属性名,调用 getProperty 方法获取对应值 
	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;
		}
	}

	@Override
	public 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) {
			this.activeProfiles.clear();
			for (String profile : profiles) {
				validateProfile(profile);
				this.activeProfiles.add(profile);
			}
		}
	}

profiles 对应的方法相对比较简单,根据“默认字段名”读取对应的值后保存

getSystemProperties、getSystemEnvironment
	// 返回 System.getProperties(),权限异常时委托给 ReadOnlySystemAttributesMap
	// ReadOnlySystemAttributesMap 内部的 get 方法委托给了 getSystemAttribute -> System.getProperty
	@Override
	@SuppressWarnings({"rawtypes", "unchecked"})
	public Map<String, Object> getSystemProperties() {
		try {
			return (Map) System.getProperties();
		}
		catch (AccessControlException ex) {
			return (Map) new ReadOnlySystemAttributesMap() {
				@Override
				@Nullable
				protected 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;
					}
				}
			};
		}
	}

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

实现 getSystemPropertiesgetSystemEnvironment 供子类调用,主要是把 System.propertiesSystem.getenv 的属性封装成 map 返回

ReadOnlySystemAttributesMap 一个很好用的只读 Map
PropertyResolver部分方法
	@Override
	public boolean containsProperty(String key) {
		return this.propertyResolver.containsProperty(key);
	}

	@Override
	@Nullable
	public String getProperty(String key) {
		return this.propertyResolver.getProperty(key);
	}

	@Override
	public String getProperty(String key, String defaultValue) {
		return this.propertyResolver.getProperty(key, defaultValue);
	}

可以发现,PropertyResolver 相关方法则是全部委托给了 ConfigurablePropertyResolverPropertySourcesPropertyResolver)来实现,贴出了一部分参考

private final ConfigurablePropertyResolver propertyResolver =
			new PropertySourcesPropertyResolver(this.propertySources);

父类构造方法被调用的时候,通过 钩子方法 收集子类的 个性化 定制,然后将定制的 propertySources 交给 PropertySourcesPropertyResolver 统一解析处理,抽象父类 的教科书演示

StandardEnvironment
public class StandardEnvironment extends AbstractEnvironment {

	public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

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

}

类如其名,标准实现,将父类定义的 systemPropertiessystemEnvironment 封装成 PropertiesPropertySourceSystemEnvironmentPropertySource,定制到 MutablePropertySources

StandardServletEnvironment
public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {

	public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";

	public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";

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

	public static void initServletPropertySources(MutablePropertySources sources,
			@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {

		Assert.notNull(sources, "'propertySources' must not be null");
		String name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;
		if (servletContext != null && sources.get(name) instanceof StubPropertySource) {
			sources.replace(name, new ServletContextPropertySource(name, servletContext));
		}
		name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;
		if (servletConfig != null && sources.get(name) instanceof StubPropertySource) {
			sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
		}
	}
}

在父类的基础上又维护了几个 PropertySource,并在 initServletPropertySources 方法中将 StubPropertySource 选择性的替换为 ServletContextPropertySourceServletConfigPropertySource

示例 demo

@Configuration
@ComponentScan("com.xsn.env")
public class EnvConfig {

	@Component
	public class EnvObj {

		@Autowired
		Environment environment;

		void env() throws IOException {
			/*((ConfigurableEnvironment) environment)
					.getSystemEnvironment()
					.forEach((k, v) -> System.out.println(k +":"+ v));

			System.out.println("==================================");

			((ConfigurableEnvironment) environment)
					.getSystemProperties()
					.forEach((k, v) -> System.out.println(k +":"+ v));

			System.out.println("==================================");*/

			((ConfigurableEnvironment) environment).setActiveProfiles("test");
			for (String activeProfile : ((ConfigurableEnvironment) environment).getActiveProfiles()) {
				System.out.println(activeProfile);
			}

			System.out.println("==================================");

			((ConfigurableEnvironment) environment)
					.getPropertySources()
					.addLast(new MapPropertySource(
							"my",
							new HashMap<String, Object>() {
								{
									put("name", "dd");
								}
							}
					));

			((ConfigurableEnvironment) environment)
					.getPropertySources()
					.addLast(new ResourcePropertySource("my2", "classpath:application.properties"));

			System.out.println(((ConfigurableEnvironment) environment)
					.getProperty("test"));
			System.out.println(((ConfigurableEnvironment) environment)
					.getProperty("name"));
			System.out.println(((ConfigurableEnvironment) environment)
					.getProperty("testCondition"));

			System.out.println("==================================");

			((ConfigurableEnvironment) environment)
					.getPropertySources()
					.iterator()
					.forEachRemaining(i -> System.out.println(i));

		}
	}
}

@Component
@Conditional(EnvCondition.class)
public class EnvConditionBean {
}

public class EnvCondition implements Condition {

	public EnvCondition() {
	}

	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		/**
		 * 可以发现,在 Condition 条件中,我们可以干涉 Environment
		 */
		((ConfigurableEnvironment) context.getEnvironment())
				.getPropertySources()
				.addLast(new MapPropertySource(
						"testCondition",
						new HashMap<String, Object>() {
							{
								put("testCondition", "test");
							}
						}
				));

		return true;
	}
}

@Test
public void test1() throws IOException {
	AnnotationConfigApplicationContext ac =
			new AnnotationConfigApplicationContext(EnvConfig.class);

	EnvConfig.EnvObj bean = ac.getBean(EnvConfig.EnvObj.class);
	bean.env();

}

结果:(可以看到 Condition 中添加的属性也生效了)
test
==================================
default
dd
test
==================================
PropertiesPropertySource {name='systemProperties'}
SystemEnvironmentPropertySource {name='systemEnvironment'}
MapPropertySource {name='testCondition'}
MapPropertySource {name='my'}
ResourcePropertySource {name='my2'}

类图

environment

序列图

画个 序列图 回顾下 AbstractEnvironment 类的设计

AbstractEnvironment

总结

本章节对 Environment 及其大部分子类做了解读,帮助我们在使用 Spring 的过程中更加灵活的使用它

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值