提交 143db0d8 编写于 作者: C Chris Beams

Introduce SystemEnvironmentPropertySource

Properties such as 'spring.profiles.active' cannot be specified at the
command line under Bash and other shells due to variable naming
constraints. This change allows for exchanging underscores for periods
as well as capitalizing property names for more idiomatic naming when
dealing with environment variables.

For example, Spring will respect equally either of the following:

    spring.profiles.active=p1 java -classpath ... MyApp

    SPRING_PROFILES_ACTIVE=p1 java -classpath ... MyApp

The former is not possible under Bash, while the latter is. No code or
configuration changes are required; SystemEnvironmentPropertySource
adapts for these varations automatically.

SystemEnvironmentPropertySource is added by default as
"systemEnvironment" to StandardEnvironment and all subtypes, taking the
place of the plain MapPropertySource that was in use before this change.

Issue: SPR-8869
上级 2c26a23c
......@@ -16,6 +16,8 @@
package org.springframework.core.env;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
/**
......@@ -44,6 +46,8 @@ public abstract class EnumerablePropertySource<T> extends PropertySource<T> {
protected static final String[] EMPTY_NAMES_ARRAY = new String[0];
protected final Log logger = LogFactory.getLog(getClass());
public EnumerablePropertySource(String name, T source) {
super(name, source);
......@@ -65,9 +69,15 @@ public abstract class EnumerablePropertySource<T> extends PropertySource<T> {
Assert.notNull(name, "property name must not be null");
for (String candidate : this.getPropertyNames()) {
if (candidate.equals(name)) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("PropertySource [%s] contains '%s'", getName(), name));
}
return true;
}
}
if (logger.isTraceEnabled()) {
logger.trace(String.format("PropertySource [%s] does not contain '%s'", getName(), name));
}
return false;
}
......
......@@ -44,6 +44,7 @@ package org.springframework.core.env;
* @author Chris Beams
* @since 3.1
* @see ConfigurableEnvironment
* @see SystemEnvironmentPropertySource
* @see org.springframework.web.context.support.StandardServletEnvironment
*/
public class StandardEnvironment extends AbstractEnvironment {
......@@ -71,7 +72,7 @@ public class StandardEnvironment extends AbstractEnvironment {
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(new MapPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
}
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.env;
import java.util.Map;
import org.springframework.util.Assert;
/**
* Specialization of {@link MapPropertySource} designed for use with
* {@linkplain AbstractEnvironment#getSystemEnvironment() system environment variables}.
* Compensates for constraints in Bash and other shells that do not allow for variables
* containing the period character; also allows for uppercase variations on property
* names for more idiomatic shell use.
*
* <p>For example, a call to {@code getProperty("foo.bar")} will attempt to find a value
* for the original property or any 'equivalent' property, returning the first found:
* <ul>
* <li>{@code foo.bar} - the original name</li>
* <li>{@code foo_bar} - with underscores for periods (if any)</li>
* <li>{@code FOO.BAR} - original, with upper case</li>
* <li>{@code FOO_BAR} - with underscores and upper case</li>
* </ul>
*
* The same applies for calls to {@link #containsProperty(String)}, which returns
* {@code true} if any of the above properties are present, otherwise {@code false}.
*
* <p>This feature is particularly useful when specifying active or default profiles as
* environment variables. The following is not allowable under Bash
*
* <pre class="code">spring.profiles.active=p1 java -classpath ... MyApp</pre>
*
* However, the following syntax is permitted and is also more conventional.
*
* <pre class="code">SPRING_PROFILES_ACTIVE=p1 java -classpath ... MyApp</pre>
*
* <p>Enable debug- or trace-level logging for this class (or package) for messages
* explaining when these 'property name resolutions' occur.
*
* <p>This property source is included by default in {@link StandardEnvironment} and all
* its subclasses.
*
* @author Chris Beams
* @since 3.1
* @see StandardEnvironment
* @see AbstractEnvironment#getSystemEnvironment()
* @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
*/
public class SystemEnvironmentPropertySource extends MapPropertySource {
/**
* Create a new {@code SystemEnvironmentPropertySource} with the given name and
* delegating to the given {@code MapPropertySource}.
*/
public SystemEnvironmentPropertySource(String name, Map<String, Object> source) {
super(name, source);
}
/**
* Return true if a property with the given name or any underscore/uppercase variant
* thereof exists in this property source.
*/
@Override
public boolean containsProperty(String name) {
return resolvePropertyName(name) != null;
}
/**
* {@inheritDoc}
* <p>This implementation returns {@code true} if a property with the given name or
* any underscore/uppercase variant thereof exists in this property source.
*/
@Override
public Object getProperty(String name) {
Assert.notNull(name, "property name must not be null");
String actualName = resolvePropertyName(name);
if (actualName == null) {
// at this point we know the property does not exist
return null;
}
if (logger.isDebugEnabled() && !name.equals(actualName)) {
logger.debug(String.format(
"PropertySource [%s] does not contain '%s', but found equivalent '%s'",
this.getName(), name, actualName));
}
return super.getProperty(actualName);
}
/**
* Check to see if this property source contains a property with the given name, or
* any underscore / uppercase variation thereof. Return the resolved name or
* {@code null} if none found.
*/
private String resolvePropertyName(String name) {
if (super.containsProperty(name)) {
return name;
}
String usName = name.replace('.', '_');
if (!name.equals(usName) && super.containsProperty(usName)) {
return usName;
}
String ucName = name.toUpperCase();
if (!name.equals(ucName)) {
if (super.containsProperty(ucName)) {
return ucName;
} else {
String usUcName = ucName.replace('.', '_');
if (!ucName.equals(usUcName) && super.containsProperty(usUcName)) {
return usUcName;
}
}
}
return null;
}
}
......@@ -71,6 +71,13 @@ public class StandardEnvironmentTests {
assertThat(sources.size(), is(2));
}
@Test
public void propertySourceTypes() {
ConfigurableEnvironment env = new StandardEnvironment();
MutablePropertySources sources = env.getPropertySources();
assertThat(sources.get(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME), instanceOf(SystemEnvironmentPropertySource.class));
}
@Test
public void activeProfilesIsEmptyByDefault() {
assertThat(environment.getActiveProfiles().length, is(0));
......
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.env;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
/**
* Unit tests for {@link SystemEnvironmentPropertySource}.
*
* @author Chris Beams
* @since 3.1
*/
public class SystemEnvironmentPropertySourceTests {
private Map<String, Object> envMap;
private PropertySource<?> ps;
@Before
public void setUp() {
envMap = new HashMap<String, Object>();
ps = new SystemEnvironmentPropertySource("sysEnv", envMap);
}
@Test
public void none() {
//envMap.put("a.key", "a_value");
assertThat(ps.containsProperty("a.key"), equalTo(false));
assertThat(ps.getProperty("a.key"), equalTo(null));
}
@Test
public void normalWithoutPeriod() {
envMap.put("akey", "avalue");
assertThat(ps.containsProperty("akey"), equalTo(true));
assertThat(ps.getProperty("akey"), equalTo((Object)"avalue"));
}
@Test
public void normalWithPeriod() {
envMap.put("a.key", "a.value");
assertThat(ps.containsProperty("a.key"), equalTo(true));
assertThat(ps.getProperty("a.key"), equalTo((Object)"a.value"));
}
@Test
public void withUnderscore() {
envMap.put("a_key", "a_value");
assertThat(ps.containsProperty("a_key"), equalTo(true));
assertThat(ps.containsProperty("a.key"), equalTo(true));
assertThat(ps.getProperty("a_key"), equalTo((Object)"a_value"));
assertThat( ps.getProperty("a.key"), equalTo((Object)"a_value"));
}
@Test
public void withBothPeriodAndUnderscore() {
envMap.put("a_key", "a_value");
envMap.put("a.key", "a.value");
assertThat(ps.getProperty("a_key"), equalTo((Object)"a_value"));
assertThat( ps.getProperty("a.key"), equalTo((Object)"a.value"));
}
@Test
public void withUppercase() {
envMap.put("A_KEY", "a_value");
assertThat(ps.containsProperty("A_KEY"), equalTo(true));
assertThat(ps.containsProperty("A.KEY"), equalTo(true));
assertThat(ps.containsProperty("a_key"), equalTo(true));
assertThat(ps.containsProperty("a.key"), equalTo(true));
assertThat(ps.getProperty("A_KEY"), equalTo((Object)"a_value"));
assertThat(ps.getProperty("A.KEY"), equalTo((Object)"a_value"));
assertThat(ps.getProperty("a_key"), equalTo((Object)"a_value"));
assertThat(ps.getProperty("a.key"), equalTo((Object)"a_value"));
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册