前言 org.springframework.core.env.Environment
是当前应用运行环境的公开接口,主要包括应用程序运行环境的两个关键方面:配置文件(profiles)和属性(properties)。Environment
继承自接口PropertyResolver
,而PropertyResolver
提供了属性访问的相关方法。这篇文章从源码的角度分析Environment
的存储容器和加载流程,然后基于源码的理解给出一个生产级别的扩展。
哪里创建的Environment? 学习过springboot的都知道,在Springboot的main入口函数中调用SpringApplication.run(DemoApplication.class,args)函数便可以启用SpringBoot应用程序,跟踪一下SpringApplication源码可以发现,最终还是调用了SpringApplication的动态run函数。
我们在编写一个spring boot应用时通常启动的方式是通过SpringApplication.run(xxx.class, args)
来启动的,
1 2 3 4 5 6 @SpringBootApplication public class ClientApplication { public static void main (String[] args) { SpringApplication.run(ClientApplication.class , args ) ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 ```java public class SpringApplication{ // ...省略与Environment无关的代码 public SpringApplication(Class... primarySources) { this(null, primarySources); } public static ConfigurableApplicationContext run(Class primarySource, String... args) { return run(new Class[] { primarySource }, args); } public static ConfigurableApplicationContext run(Class[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); } public ConfigurableApplicationContext run(String... args) { // ...省略与Environment无关的代码 try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 与Environment相关的关键代码 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); ...省略与Environment无关的代码 } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } // ...省略与Environment无关的代码 } private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 关键代码:获取ConfigurableEnvironment, 如果不存在就创建 ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; } // 根据应用类型创建相应的ConfigurableEnvironment private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } switch (this.webApplicationType) { case SERVLET: // servlet应用 return new StandardServletEnvironment(); case REACTIVE: // reactive反应式应用 return new StandardReactiveWebEnvironment(); default: // 非reactive, 非web, 也就是标准java应用 return new StandardEnvironment(); } } // ...省略与Environment无关的代码 }
可以看到SpringApplication
类中最终会根据应用类型(WebApplicationType
枚举类)创建相应的ConfigurableEnvironment
的具体实现实例对象,另外关于SpringApplication这个类的具体执行流程另一篇博客已有详细的源码分析,这里不再赘述,参见Spring Boot启动流程分析
下面以web环境的StandardServletEnvironment
为例进行分析
Environment类体系
PropertyResolver
:用于针对任何基础源解析属性的接口,提供属性访问功能
ConfigurablePropertyResolver
:继承自PropertyResolver
,额外提供属性类型转换(基于org.springframework.core.convert.ConversionService
)功能
Environment
:继承自PropertyResolver
,额外提供访问和判断profiles的功能
ConfigurableEnvironment
:继承自ConfigurablePropertyResolver
和Environment
,并且提供设置激活的profile和默认的profile的功能。
ConfigurableWebEnvironment
:继承自ConfigurableEnvironment
,并且提供配置Servlet
上下文和Servlet
参数的功能。
AbstractEnvironment
:实现了ConfigurableEnvironment
接口,默认属性和存储容器的定义,并且实现了ConfigurableEnvironment
中的方法,并且为子类预留可重写的扩展方法。
StandardEnvironment
:继承自AbstractEnvironment
,非Servlet
(Web)环境下的标准Environment
实现。
StandardServletEnvironment
:继承自StandardEnvironment
,Servlet
(Web)环境下的标准Environment
实现。
MockEnvironment
: ConfigurableEnvironment
的简单实现,出于测试的目的,用于暴露setProperty(String, String)
及 withProperty(String, String)
方法
AbstractPropertyResolver
:抽象基类,用于根据任何基础源解析属性, conversionService
的默认实现使用DefaultConversionService
创建。
PropertySourcesPropertyResolver
:PropertyResolver
的实现,可以针对一组基础的PropertySource
解析属性值
reactive相关的暂时不研究。
Environment提供的方法 一般情况下,我们在SpringMVC项目中启用到的是StandardServletEnvironment
,它的父接口是ConfigurableWebEnvironment
,我们可以查看此接口提供的方法:
PropertyResolover
:从接口提供的方法可以看到PropertyResolover
接口提供的方法都是获取属性相关的get方法或者求值(resolve)方法,不涉及set操作
ConfigurablePropertyResolover
:扩展了PropertyResolover
接口提供的方法, 额外提供了访问属性转换(ConfigurableConversionService
)的get/set方法,及属性占位符placheholder
的get/set方法,即该类的目的是提供写操作(set)的相关方法
Environment
:从提供的方法可以看到全部是与profile相关的访问,获取当前默认的profile及已激活的profile, 只有get操作
ConfigurableEnvironment
:从类名就知道提供的对Environment
写操作相关的方法,包括设置默认profile、设置当前激活哪个profile、获取系统属性、合并另一个ConfigurableEnvironment
的属性
ConfigurableWebEnvironment
:只提供了initPropertySources
方法, 从参数就可以看出来是提供配置ServletContext
上下文和ServletConfig
参数的功能
因此从每个类提供的方法可以看到Environment相关的接口的思想是一层层抽象出标准环境、web环境、可配置的标准环境、可配置的web环境,将读操作和写操作分离、web环境和非web环境分离
Environment的存储容器 从SpringApplication.getOrCreateEnvironment()
方法可以看到最终返回的ConfigurableEnvironment
就是StandardServletEnvironment
或StandardEnvironment
或者StandardReactiveWebEnvironment
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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(); } }
下面从StandardServletEnvironment开始分析。
new StandardServletEnvironment()会执行StandardServletEnvironment的的构造方法,会发现这个类没有提供构造方法,只重写了customizePropertySources
及initPropertySources
这2个方法,这2个类后续分析。
我们继续看StandardServletEnvironment
的父类StandardEnvironment
的实现,发现StandardEnvironment
也是非常简单,只重写了customizePropertySources()
这个方法,暂且不管,继续看它的父类AbstractEnvironment
,会发现这个类做了很多事情,定义了propertySources及profile相关属性,下面具体分析AbstractEnvironment
这个类。
PropertySource 要分析AbstractEnvironment这个类先看PropertySource
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public abstract class AbstractEnvironment implements ConfigurableEnvironment { private final MutablePropertySources propertySources = new MutablePropertySources(); public AbstractEnvironment () { customizePropertySources(this .propertySources); } protected void customizePropertySources (MutablePropertySources propertySources) { } }
上面的propertySource
属性就是用来存放PropertySource
列表的,再看MutablePropertySources
这个类的实现:
1 2 3 4 5 6 public class MutablePropertySources implements PropertySources { private final List> propertySourceList = new CopyOnWriteArrayList<>(); ...省略代码 }
可以看到其中维护了PropertySource这个类的集合propertySourceList属性,这个就是最底层的存储容器。继续跟进看PropertySource的源码实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public abstract class PropertySource <T > { protected final String name; protected final T source; public PropertySource (String name, T source) { Assert.hasText(name, "Property source name must contain at least one character" ); Assert.notNull(source, "Property source must not be null" ); this .name = name; this .source = source; } public PropertySource (String name) { this (name, (T) new Object()); } public String getName () { return this .name; } public T getSource () { return this .source; } public boolean containsProperty (String name) { return (getProperty(name) != null ); } @Nullable public abstract Object getProperty (String name) ; @Override public boolean equals (@Nullable Object other) { return (this == other || (other instanceof PropertySource && ObjectUtils.nullSafeEquals(this .name, ((PropertySource) other).name))); } @Override public int hashCode () { return ObjectUtils.nullSafeHashCode(this .name); } }
源码相对简单,预留了一个getProperty
抽象方法给子类实现,重点需要关注的是重写了的equals
和hashCode
方法,实际上只和name
属性相关,这一点很重要,说明一个PropertySource实例绑定到一个唯一的name,这个name有点像HashMap里面的key ,部分移除、判断方法都是基于name属性。PropertySource
的最常用子类是MapPropertySource
、PropertiesPropertySource
、ResourcePropertySource
、StubPropertySource
、ComparisonPropertySource
。
MapPropertySource
source指定为Map类型的PropertySource
实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class MapPropertySource extends EnumerablePropertySource <Map <String , Object >> { public MapPropertySource (String name, Map source) { super (name, source); } @Override @Nullable public Object getProperty (String name) { return this .source.get(name); } @Override public boolean containsProperty (String name) { return this .source.containsKey(name); } @Override public String[] getPropertyNames() { return StringUtils.toStringArray(this .source.keySet()); } }
PropertiesPropertySource
source指定为Properties
类型的PropertySource
实现,PropertiesPropertySource
继承了MapPropertySource
,说明Properties
这个类本身是集合Map
的子类, 查看Properties
类的源码可以发现Properties
这个类继承了Hashtable
, 而HashTable
又实现了Map
接口,所以PropertiesPropertySource
是MapPropertySource
的特殊化类型实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class PropertiesPropertySource extends MapPropertySource { @SuppressWarnings ({"rawtypes" , "unchecked" }) public PropertiesPropertySource (String name, Properties source) { super (name, (Map) source); } protected PropertiesPropertySource (String name, Map source) { super (name, source); } @Override public String[] getPropertyNames() { synchronized (this .source) { return super .getPropertyNames(); } } }
ResourcePropertySource
继承自PropertiesPropertySource
,source指定为通过Resource
实例转化为Properties
再转换为Map实例。
1 2 3 4 5 6 7 8 9 10 11 12 public class ResourcePropertySource extends PropertiesPropertySource { @Nullable private final String resourceName; public ResourcePropertySource (String name, EncodedResource resource) throws IOException { super (name, PropertiesLoaderUtils.loadProperties(resource)); this .resourceName = getNameForResource(resource.getResource()); } }
StubPropertySource
PropertySource
的一个内部类,source设置为null,实际上就是空实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public abstract class PropertySource <T > { protected final String name; protected final T source; public PropertySource (String name, T source) { Assert.hasText(name, "Property source name must contain at least one character" ); Assert.notNull(source, "Property source must not be null" ); this .name = name; this .source = source; } public static class StubPropertySource extends PropertySource <Object > { public StubPropertySource (String name) { super (name, new Object()); } @Override @Nullable public String getProperty (String name) { return null ; } } }
ComparisonPropertySource
继承自ComparisonPropertySource
,所有属性访问方法强制抛出异常,作用就是一个不可访问属性的空实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public abstract class PropertySource <T > { protected final String name; protected final T source; ...省略代码 static class ComparisonPropertySource extends StubPropertySource { private static final String USAGE_ERROR = "ComparisonPropertySource instances are for use with collection comparison only" ; public ComparisonPropertySource (String name) { super (name); } @Override public Object getSource () { throw new UnsupportedOperationException(USAGE_ERROR); } @Override public boolean containsProperty (String name) { throw new UnsupportedOperationException(USAGE_ERROR); } @Override @Nullable public String getProperty (String name) { throw new UnsupportedOperationException(USAGE_ERROR); } } }
AbstractEnvironment 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 public abstract class AbstractEnvironment implements ConfigurableEnvironment { 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 activeProfiles = new LinkedHashSet<>(); private final Set 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) { } protected Set getReservedDefaultProfiles () { return Collections.singleton(RESERVED_DEFAULT_PROFILE_NAME); } @Override public String[] getActiveProfiles() { return StringUtils.toStringArray(doGetActiveProfiles()); } protected Set 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); } } } @Override public MutablePropertySources getPropertySources () { return this .propertySources; } @Override public ConfigurableConversionService getConversionService () { return this .propertyResolver.getConversionService(); } @Override @Nullable public String getProperty (String key) { return this .propertyResolver.getProperty(key); } @Override public String resolvePlaceholders (String text) { return this .propertyResolver.resolvePlaceholders(text); } }
可以看到AbstractEnvironment这个类的目的就是提供Environment的抽象实现,实现了Environment的一些通用的方法,但是具体的PropertySource是怎么加入到MutablePropertySources的是由子类去处理的,所以MutablePropertySources肯定提供了一些add、remove的方法, 下面分析MutablePropertySources这个类的源码。
MutablePropertySources MutablePropertySources
类中propertySourceList
属性时最底层的存储容器,也就是环境属性都是存放在一个CopyOnWriteArrayList>
实例中。
该类的意图:Mutable的意思是可变的,所以说明该类的属性是可以进行写操作的, 聚合了多个属性源
MutablePropertySources
实现了PropertySources
接口,先看些PropertySources
接口的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public interface PropertySources extends Iterable <PropertySource > { default Stream> stream() { return StreamSupport.stream(spliterator(), false ); } boolean contains (String name) ; @Nullable PropertySource get(String name); }
注意PropertySources继承了集合的Iterable接口,说明它是可迭代的(可以使用增强的for循环遍历)
MutablePropertySources
提供了get(String name)
、addFirst
、addLast
、addBefore
、addAfter
、remove
、replace
等便捷方法,方便操作propertySourceList
集合的元素,这里挑选addBefore
的源码分析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 public class MutablePropertySources implements PropertySources { private final List> propertySourceList = new CopyOnWriteArrayList<>(); public MutablePropertySources () { } public MutablePropertySources (PropertySources propertySources) { this (); for (PropertySource propertySource : propertySources) { addLast(propertySource); } } @Override public Iterator> iterator() { return this .propertySourceList.iterator(); } @Override public Stream> stream() { return this .propertySourceList.stream(); } @Override @Nullable public PropertySource get(String name) { int index = this .propertySourceList.indexOf(PropertySource.named(name)); return (index != -1 ? this .propertySourceList.get(index) : null ); } public void addBefore (String relativePropertySourceName, PropertySource propertySource) { assertLegalRelativeAddition(relativePropertySourceName, propertySource); removeIfPresent(propertySource); int index = assertPresentAndGetIndex(relativePropertySourceName); addAtIndex(index, propertySource); } protected void assertLegalRelativeAddition (String relativePropertySourceName, PropertySource propertySource) { String newPropertySourceName = propertySource.getName(); if (relativePropertySourceName.equals(newPropertySourceName)) { throw new IllegalArgumentException( "PropertySource named '" + newPropertySourceName + "' cannot be added relative to itself" ); } } protected void removeIfPresent (PropertySource propertySource) { this .propertySourceList.remove(propertySource); } private int assertPresentAndGetIndex (String name) { int index = this .propertySourceList.indexOf(PropertySource.named(name)); if (index == -1 ) { throw new IllegalArgumentException("PropertySource named '" + name + "' does not exist" ); } return index; } private void addAtIndex (int index, PropertySource propertySource) { removeIfPresent(propertySource); this .propertySourceList.add(index, propertySource); } }
MutablePropertySources使用示例 大多数PropertySource
子类的修饰符都是public,可以直接使用,这里写个小demo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package com.calebzhao.test;import org.springframework.core.env.MapPropertySource;import org.springframework.core.env.MutablePropertySources;import org.springframework.core.env.PropertiesPropertySource;import java.util.HashMap;import java.util.Map;import java.util.Properties;public class MutablePropertySourceDemo { public static void main (String[] args) { Map data1 = new HashMap<>(); data1.put("a" , 1 ); data1.put("b" , 2 ); MapPropertySource mapPropertySource = new MapPropertySource("p1" , data1); Properties properties = new Properties(); properties.put("AA" , 11 ); properties.put("BB" , 22 ); PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource("p2" , properties); MutablePropertySources mutablePropertySources = new MutablePropertySources(); mutablePropertySources.addLast(mapPropertySource); mutablePropertySources.addBefore("p1" , propertiesPropertySource); System.out.println(mutablePropertySources); } }
输出如下:
Environment属性访问源码分析 在分析AbstractEnvionment源码时没有分析属性的访问是如何实现,当时只是在getProperty()方法上注明:委托给了PropertySourcesPropertyResolver,源码后续分析,这里先回顾下AbstractEnvironment的访问属性相关的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public abstract class AbstractEnvironment implements ConfigurableEnvironment { private final MutablePropertySources propertySources = new MutablePropertySources(); private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this .propertySources); public AbstractEnvironment () { customizePropertySources(this .propertySources); } protected void customizePropertySources (MutablePropertySources propertySources) { } @Override @Nullable public String getProperty (String key) { return this .propertyResolver.getProperty(key); } @Override public String resolvePlaceholders (String text) { return this .propertyResolver.resolvePlaceholders(text); } }
上面提到过,都是委托到PropertySourcesPropertyResolver
,先看它的构造函数:
1 2 3 4 5 6 7 8 9 10 11 public class PropertySourcesPropertyResolver extends AbstractPropertyResolver { @Nullable private final PropertySources propertySources; public PropertySourcesPropertyResolver (@Nullable PropertySources propertySources) { this .propertySources = propertySources; } ...省略代码 }
只依赖于一个PropertySources
实例,这个propertySources
就是MutablePropertySources
的实例。重点分析一下PropertySourcesPropertyResolver
类最复杂的一个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 public class PropertySourcesPropertyResolver extends AbstractPropertyResolver { @Nullable protected T getProperty (String key, Class 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 ; } @Nullable protected T convertValueIfNecessary (Object value, @Nullable Class 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); } ...省略代码 }
这里的源码告诉我们,如果出现多个PropertySource
中存在同名的key,返回的是第一个PropertySource
对应key的属性值的处理结果,因此我们如果需要自定义一些环境属性,需要十分清楚各个PropertySource
的顺序。
StandardEnvironment 表示“标准”(即非web)应用程序的Environment实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 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())); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 ```java public abstract class AbstractEnvironment implements ConfigurableEnvironment { // 获得JVM系统属性,与jvm相关的, 比如java --server.port=8080 -jar xx.jar等可以针对每个jvm单独设置的 @Override @SuppressWarnings({"rawtypes", "unchecked"}) public Map 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; } } }; } } // 获得系统环境变量,与操作系统相关的, 比如user.dir、path、JAVA_HOME等操作系统上配置的全局属性 @Override @SuppressWarnings({"rawtypes", "unchecked"}) public Map 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; } } }; } } }
StandardServletEnvironment servlet环境的Environment
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 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); } }
来看下WebApplicationContextUtils.initServletPropertySources方法的实现源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 public abstract class WebApplicationContextUtils { @Nullable public static WebApplicationContext getWebApplicationContext (ServletContext sc) { return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); } @Nullable public static WebApplicationContext getWebApplicationContext (ServletContext sc, String attrName) { Assert.notNull(sc, "ServletContext must not be null" ); Object attr = sc.getAttribute(attrName); if (attr == null ) { return null ; } if (attr instanceof RuntimeException) { throw (RuntimeException) attr; } if (attr instanceof Error) { throw (Error) attr; } if (attr instanceof Exception) { throw new IllegalStateException((Exception) attr); } if (!(attr instanceof WebApplicationContext)) { throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr); } return (WebApplicationContext) attr; } 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.contains(name) && sources.get(name) instanceof StubPropertySource) { sources.replace(name, new ServletContextPropertySource(name, servletContext)); } name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME; if (servletConfig != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) { sources.replace(name, new ServletConfigPropertySource(name, servletConfig)); } } }
总结 看到这里再回过头看Environment的类体系,想一想每个类的设计意图,做了哪些事情