Support default profile (SPR-7508, SPR-7778)

'default' is now a reserved profile name, indicating
that any beans defined within that profile will be registered
unless another profile or profiles have been activated.

Examples below are expressed in XML, but apply equally when
using the @Profile annotation.

EXAMPLE 1:

        <beans>
            <beans profile="default">
                <bean id="foo" class="com.acme.EmbeddedFooImpl"/>
            </beans>
            <beans profile="production">
                <bean id="foo" class="com.acme.ProdFooImpl"/>
            </beans>
        </beans>

    In the case above, the EmbeddedFooImpl 'foo' bean will be
    registered if:
        a) no profile is active
        b) the 'default' profile has explicitly been made active

    The ProdFooImpl 'foo' bean will be registered if the 'production'
    profile is active.

EXAMPLE 2:

        <beans profile="default,xyz">
            <bean id="foo" class="java.lang.String"/>
        </beans>

    Bean 'foo' will be registered if any of the following are true:
        a) no profile is active
        b) 'xyz' profile is active
        c) 'default' profile has explicitly been made active
        d) both (b) and (c) are true

Note that the default profile is not to be confused with specifying no
profile at all.  When the default profile is specified, beans are
registered only if no other profiles are active; whereas when no profile
is specified, bean definitions are always registered regardless of which
profiles are active.

The default profile may be configured programmatically:

    environmnent.setDefaultProfile("embedded");

or declaratively through any registered PropertySource, e.g. system properties:

    -DdefaultSpringProfile=embedded

Assuming either of the above, example 1 could be rewritten as follows:

        <beans>
            <beans profile="embedded">
                <bean id="foo" class="com.acme.EmbeddedFooImpl"/>
            </beans>
            <beans profile="production">
                <bean id="foo" class="com.acme.ProdFooImpl"/>
            </beans>
        </beans>

It is unlikely that use of the default profile will make sense in
conjunction with a statically specified 'springProfiles' property.
For example, if 'springProfiles' is specified as a web.xml context
param, that profile will always be active for that application,
negating the possibility of default profile bean definitions ever
being registered.

The default profile is most useful for ensuring that a valid set of
bean definitions will always be registered without forcing users
to explictly specify active profiles.  In the embedded vs. production
examples above, it is assumed that the application JVM will be started
with -DspringProfiles=production when the application is in fact in
a production environment.  Otherwise, the embedded/default profile bean
definitions will always be registered.


git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@3804 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Chris Beams 2010-12-01 09:01:58 +00:00
parent 839cb85688
commit f455b1e89a
15 changed files with 249 additions and 129 deletions

View File

@ -112,24 +112,13 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume
protected void doRegisterBeanDefinitions(Element root) { protected void doRegisterBeanDefinitions(Element root) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
boolean isCandidate = false; if (StringUtils.hasText(profileSpec)) {
if (profileSpec == null || profileSpec.equals("")) { String[] specifiedProfiles = commaDelimitedListToStringArray(trimAllWhitespace(profileSpec));
isCandidate = true; if (!this.environment.acceptsProfiles(specifiedProfiles)) {
} else { // TODO SPR-7508: log that this bean is being rejected on profile mismatch
String[] profiles = commaDelimitedListToStringArray(trimAllWhitespace(profileSpec));
for (String profile : profiles) {
if (this.environment.getActiveProfiles().contains(profile)) {
isCandidate = true;
break;
}
}
}
if (!isCandidate) {
// TODO SPR-7508 logging
// logger.debug(format("XML is targeted for environment [%s], but current environment is [%s]. Skipping", targetEnvironment, environment == null ? null : environment.getName()));
return; return;
} }
}
// any nested <beans> elements will cause recursion in this method. in // any nested <beans> elements will cause recursion in this method. in
// order to propagate and preserve <beans> default-* attributes correctly, // order to propagate and preserve <beans> default-* attributes correctly,

View File

@ -81,7 +81,14 @@
<xsd:attribute name="profile" use="optional" type="xsd:string"> <xsd:attribute name="profile" use="optional" type="xsd:string">
<xsd:annotation> <xsd:annotation>
<xsd:documentation><![CDATA[ <xsd:documentation><![CDATA[
TODO:SPR-7508: Document profile annotation (may be comma-delimited) TODO:SPR-7508: Document profile annotation:
* may be comma-delimited
* empty profile means beans will always be registered
* profile="default" means that beans will be registered unless other profile(s) are active
* profile="xyz,default" means that beans will be registered if 'xyz' is active or if no profile is active
* ConfigurableEnvironment.setDefaultProfileName(String) customizes the name of the default profile
* 'defaultSpringProfile' property customizes the name of the default profile (usually for use as a
servlet context/init param)
]]></xsd:documentation> ]]></xsd:documentation>
</xsd:annotation> </xsd:annotation>
</xsd:attribute> </xsd:attribute>

View File

@ -25,6 +25,7 @@ import org.junit.Test;
import org.junit.internal.matchers.TypeSafeMatcher; import org.junit.internal.matchers.TypeSafeMatcher;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.DefaultEnvironment; import org.springframework.core.env.DefaultEnvironment;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
@ -40,6 +41,9 @@ public class ProfileXmlBeanDefinitionTests {
private static final String ALL_ELIGIBLE_XML = "ProfileXmlBeanDefinitionTests-noProfile.xml"; private static final String ALL_ELIGIBLE_XML = "ProfileXmlBeanDefinitionTests-noProfile.xml";
private static final String MULTI_ELIGIBLE_XML = "ProfileXmlBeanDefinitionTests-multiProfile.xml"; private static final String MULTI_ELIGIBLE_XML = "ProfileXmlBeanDefinitionTests-multiProfile.xml";
private static final String UNKOWN_ELIGIBLE_XML = "ProfileXmlBeanDefinitionTests-unknownProfile.xml"; private static final String UNKOWN_ELIGIBLE_XML = "ProfileXmlBeanDefinitionTests-unknownProfile.xml";
private static final String DEFAULT_ELIGIBLE_XML = "ProfileXmlBeanDefinitionTests-defaultProfile.xml";
private static final String CUSTOM_DEFAULT_ELIGIBLE_XML = "ProfileXmlBeanDefinitionTests-customDefaultProfile.xml";
private static final String DEFAULT_AND_DEV_ELIGIBLE_XML = "ProfileXmlBeanDefinitionTests-defaultAndDevProfile.xml";
private static final String PROD_ACTIVE = "prod"; private static final String PROD_ACTIVE = "prod";
private static final String DEV_ACTIVE = "dev"; private static final String DEV_ACTIVE = "dev";
@ -80,11 +84,45 @@ public class ProfileXmlBeanDefinitionTests {
assertThat(beanFactoryFor(UNKOWN_ELIGIBLE_XML, MULTI_ACTIVE), not(containsTargetBean())); assertThat(beanFactoryFor(UNKOWN_ELIGIBLE_XML, MULTI_ACTIVE), not(containsTargetBean()));
} }
private BeanDefinitionRegistry beanFactoryFor(String xmlName, String... activeProfileNames) { @Test
public void testDefaultProfile() {
assertThat(beanFactoryFor(DEFAULT_ELIGIBLE_XML, NONE_ACTIVE), containsTargetBean());
assertThat(beanFactoryFor(DEFAULT_ELIGIBLE_XML, "other"), not(containsTargetBean()));
assertThat(beanFactoryFor(DEFAULT_AND_DEV_ELIGIBLE_XML, DEV_ACTIVE), containsTargetBean());
assertThat(beanFactoryFor(DEFAULT_AND_DEV_ELIGIBLE_XML, NONE_ACTIVE), containsTargetBean());
assertThat(beanFactoryFor(DEFAULT_AND_DEV_ELIGIBLE_XML, PROD_ACTIVE), not(containsTargetBean()));
}
@Test
public void testCustomDefaultProfile() {
{
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
ConfigurableEnvironment env = new DefaultEnvironment();
env.setDefaultProfile("custom-default");
reader.setEnvironment(env);
reader.loadBeanDefinitions(new ClassPathResource(DEFAULT_ELIGIBLE_XML, getClass()));
assertThat(beanFactory, not(containsTargetBean()));
}
{
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
ConfigurableEnvironment env = new DefaultEnvironment();
env.setDefaultProfile("custom-default");
reader.setEnvironment(env);
reader.loadBeanDefinitions(new ClassPathResource(CUSTOM_DEFAULT_ELIGIBLE_XML, getClass()));
assertThat(beanFactory, containsTargetBean());
}
}
private BeanDefinitionRegistry beanFactoryFor(String xmlName, String... activeProfiles) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
DefaultEnvironment env = new DefaultEnvironment(); DefaultEnvironment env = new DefaultEnvironment();
env.setActiveProfiles(activeProfileNames); env.setActiveProfiles(activeProfiles);
reader.setEnvironment(env); reader.setEnvironment(env);
reader.loadBeanDefinitions(new ClassPathResource(xmlName, getClass())); reader.loadBeanDefinitions(new ClassPathResource(xmlName, getClass()));
return beanFactory; return beanFactory;

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<beans profile="custom-default">
<bean id="foo" class="java.lang.String"/>
</beans>
<beans profile="other">
<!-- does not contain bean 'foo' -->
</beans>
</beans>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"
profile="dev,default">
<!-- bean should be processed if dev OR no profile is defined -->
<bean id="foo" class="java.lang.String"/>
</beans>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<beans profile="default">
<bean id="foo" class="java.lang.String"/>
</beans>
<beans profile="other">
<!-- does not contain bean 'foo' -->
</beans>
</beans>

View File

@ -106,45 +106,18 @@ public class AnnotatedBeanDefinitionReader {
registerBean(annotatedClass, null, qualifiers); registerBean(annotatedClass, null, qualifiers);
} }
private boolean hasEligibleProfile(AnnotationMetadata metadata) {
boolean hasEligibleProfile = false;
if (!metadata.hasAnnotation(Profile.class.getName())) {
hasEligibleProfile = true;
} else {
for (String profile : (String[])metadata.getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME)) {
if (this.environment.getActiveProfiles().contains(profile)) {
hasEligibleProfile = true;
break;
}
}
}
return hasEligibleProfile;
}
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) { public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
if (!hasEligibleProfile(abd.getMetadata())) { AnnotationMetadata metadata = abd.getMetadata();
if (metadata.hasAnnotation(Profile.class.getName())) {
String[] specifiedProfiles =
(String[])metadata.getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME);
if (!this.environment.acceptsProfiles(specifiedProfiles)) {
// TODO SPR-7508: log that this bean is being rejected on profile mismatch // TODO SPR-7508: log that this bean is being rejected on profile mismatch
return; return;
} }
/*
if (metadata.hasAnnotation(Profile.class.getName())) {
if (this.environment == null) {
return;
} }
Map<String, Object> profileAttribs = metadata.getAnnotationAttributes(Profile.class.getName());
String[] names = (String[]) profileAttribs.get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME);
boolean go=false;
for (String pName : names) {
if (this.environment.getActiveProfiles().contains(pName)) {
go = true;
}
}
if (!go) {
return;
}
}
*/
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName()); abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

View File

@ -37,6 +37,7 @@ import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils; import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReaderFactory;
@ -296,28 +297,18 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
} }
for (TypeFilter tf : this.includeFilters) { for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) { if (tf.match(metadataReader, this.metadataReaderFactory)) {
return hasEligibleProfile(metadataReader); AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
if (!metadata.hasAnnotation(Profile.class.getName())) {
return true;
}
String[] specifiedProfiles =
(String[])metadata.getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME);
return this.environment.acceptsProfiles(specifiedProfiles);
} }
} }
return false; return false;
} }
private boolean hasEligibleProfile(MetadataReader metadataReader) {
boolean hasEligibleProfile = false;
if (!metadataReader.getAnnotationMetadata().hasAnnotation(Profile.class.getName())) {
hasEligibleProfile = true;
} else {
for (String profile : (String[])metadataReader.getAnnotationMetadata().getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME)) {
if (this.environment.getActiveProfiles().contains(profile)) {
hasEligibleProfile = true;
break;
}
}
}
return hasEligibleProfile;
}
/** /**
* Determine whether the given bean definition qualifies as candidate. * Determine whether the given bean definition qualifies as candidate.
* <p>The default implementation checks whether the class is concrete * <p>The default implementation checks whether the class is concrete

View File

@ -101,26 +101,14 @@ class ConfigurationClassParser {
} }
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
boolean hasEligibleProfile = false; if (this.environment != null && configClass.getMetadata().hasAnnotation(Profile.class.getName())) {
if (this.environment == null) { String[] specifiedProfiles =
hasEligibleProfile = true; (String[])configClass.getMetadata().getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME);
} else { if (!this.environment.acceptsProfiles(specifiedProfiles)) {
if (!configClass.getMetadata().hasAnnotation(Profile.class.getName())) { // TODO SPR-7508: log that this bean is being rejected on profile mismatch
hasEligibleProfile = true;
} else {
for (String profile : (String[])configClass.getMetadata().getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME)) {
if (this.environment.getActiveProfiles().contains(profile)) {
hasEligibleProfile = true;
break;
}
}
}
}
if (!hasEligibleProfile) {
//logger.debug("TODO SPR-7508: issue debug statement that this class is being excluded");
// make sure XML has a symmetrical statement as well
return; return;
} }
}
AnnotationMetadata metadata = configClass.getMetadata(); AnnotationMetadata metadata = configClass.getMetadata();
while (metadata != null) { while (metadata != null) {

View File

@ -24,6 +24,16 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** /**
* TODO SPR-7508: document
*
* Components not @Profile-annotated will always be registered
* @Profile("default") means that beans will be registered unless other profile(s) are active
* @Profile({"xyz,default"}) means that beans will be registered if 'xyz' is active or if no profile is active
* ConfigurableEnvironment.setDefaultProfileName(String) customizes the name of the default profile
* 'defaultSpringProfile' property customizes the name of the default profile (usually for use as a servlet context/init param)
* ConfigurableEnvironment.setActiveProfiles(String...) sets which profiles are active
* 'springProfiles' sets which profiles are active (typically as a -D system property)
*
* @author Chris Beams * @author Chris Beams
* @since 3.1 * @since 3.1
*/ */

View File

@ -29,6 +29,7 @@ import java.util.regex.Pattern;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.DefaultEnvironment; import org.springframework.core.env.DefaultEnvironment;
import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.core.type.filter.AnnotationTypeFilter;
@ -215,8 +216,44 @@ public class ClassPathScanningCandidateComponentProviderTests {
assertThat(ctx.containsBean(ProfileAnnotatedComponent.BEAN_NAME), is(false)); assertThat(ctx.containsBean(ProfileAnnotatedComponent.BEAN_NAME), is(false));
} }
private boolean containsBeanClass(Set<BeanDefinition> candidates, Class beanClass) { @Test
for (Iterator it = candidates.iterator(); it.hasNext();) { public void testIntegrationWithAnnotationConfigApplicationContext_defaultProfile() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
// no active profiles are set
ctx.register(DefaultProfileAnnotatedComponent.class);
ctx.refresh();
assertThat(ctx.containsBean(DefaultProfileAnnotatedComponent.BEAN_NAME), is(true));
}
@Test
public void testIntegrationWithAnnotationConfigApplicationContext_defaultAndDevProfile() {
Class<?> beanClass = DefaultAndDevProfileAnnotatedComponent.class;
String beanName = DefaultAndDevProfileAnnotatedComponent.BEAN_NAME;
{
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
// no active profiles are set
ctx.register(beanClass);
ctx.refresh();
assertThat(ctx.containsBean(beanName), is(true));
}
{
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("dev");
ctx.register(beanClass);
ctx.refresh();
assertThat(ctx.containsBean(beanName), is(true));
}
{
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("other");
ctx.register(beanClass);
ctx.refresh();
assertThat(ctx.containsBean(beanName), is(false));
}
}
private boolean containsBeanClass(Set<BeanDefinition> candidates, Class<?> beanClass) {
for (Iterator<BeanDefinition> it = candidates.iterator(); it.hasNext();) {
ScannedGenericBeanDefinition definition = (ScannedGenericBeanDefinition) it.next(); ScannedGenericBeanDefinition definition = (ScannedGenericBeanDefinition) it.next();
if (beanClass.getName().equals(definition.getBeanClassName())) { if (beanClass.getName().equals(definition.getBeanClassName())) {
return true; return true;
@ -225,4 +262,16 @@ public class ClassPathScanningCandidateComponentProviderTests {
return false; return false;
} }
@Profile(AbstractEnvironment.DEFAULT_PROFILE_NAME)
@Component(DefaultProfileAnnotatedComponent.BEAN_NAME)
private static class DefaultProfileAnnotatedComponent {
static final String BEAN_NAME = "defaultProfileAnnotatedComponent";
}
@Profile({AbstractEnvironment.DEFAULT_PROFILE_NAME,"dev"})
@Component(DefaultAndDevProfileAnnotatedComponent.BEAN_NAME)
private static class DefaultAndDevProfileAnnotatedComponent {
static final String BEAN_NAME = "defaultAndDevProfileAnnotatedComponent";
}
} }

View File

@ -50,7 +50,17 @@ import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
*/ */
public abstract class AbstractEnvironment implements ConfigurableEnvironment { public abstract class AbstractEnvironment implements ConfigurableEnvironment {
public static final String SPRING_PROFILES_PROPERTY_NAME = "springProfiles"; public static final String ACTIVE_PROFILES_PROPERTY_NAME = "springProfiles";
public static final String DEFAULT_PROFILE_PROPERTY_NAME = "defaultSpringProfile";
/**
* Default name of the default profile. Override with
* {@link #setDefaultProfile(String)}.
*
* @see #setDefaultProfile(String)
*/
public static final String DEFAULT_PROFILE_NAME = "default";
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
@ -66,6 +76,8 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
private boolean explicitlySetProfiles; private boolean explicitlySetProfiles;
private String defaultProfile = DEFAULT_PROFILE_NAME;
public void addPropertySource(PropertySource<?> propertySource) { public void addPropertySource(PropertySource<?> propertySource) {
propertySources.push(propertySource); propertySources.push(propertySource);
@ -183,7 +195,7 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
if (explicitlySetProfiles) if (explicitlySetProfiles)
return; return;
String profiles = getProperty(SPRING_PROFILES_PROPERTY_NAME); String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
if (profiles == null || profiles.equals("")) { if (profiles == null || profiles.equals("")) {
return; return;
} }
@ -267,6 +279,31 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
return doResolvePlaceholders(text, strictHelper); return doResolvePlaceholders(text, strictHelper);
} }
public boolean acceptsProfiles(String[] specifiedProfiles) {
boolean activeProfileFound = false;
Set<String> activeProfiles = this.getActiveProfiles();
for (String profile : specifiedProfiles) {
if (activeProfiles.contains(profile)
|| (activeProfiles.isEmpty() && profile.equals(this.getDefaultProfile()))) {
activeProfileFound = true;
break;
}
}
return activeProfileFound;
}
public String getDefaultProfile() {
String defaultSpringProfileProperty = getProperty(DEFAULT_PROFILE_PROPERTY_NAME);
if (defaultSpringProfileProperty != null) {
return defaultSpringProfileProperty;
}
return defaultProfile;
}
public void setDefaultProfile(String defaultProfile) {
this.defaultProfile = defaultProfile;
}
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) { private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
return helper.replacePlaceholders(text, new PlaceholderResolver() { return helper.replacePlaceholders(text, new PlaceholderResolver() {
public String resolvePlaceholder(String placeholderName) { public String resolvePlaceholder(String placeholderName) {

View File

@ -29,4 +29,11 @@ public interface ConfigurableEnvironment extends Environment, PropertySourceAggr
*/ */
void setActiveProfiles(String... profiles); void setActiveProfiles(String... profiles);
/**
* Set the default profile name to be used instead of 'default'
*
* @param defaultProfile
*/
void setDefaultProfile(String defaultProfile);
} }

View File

@ -35,6 +35,19 @@ public interface Environment {
*/ */
Set<String> getActiveProfiles(); Set<String> getActiveProfiles();
/**
* TODO SPR-7508: document
*/
String getDefaultProfile();
/**
* TODO SPR-7508: document
* returns true if:
* a) one or more of specifiedProfiles are active in the given environment - see {@link #getActiveProfiles()}
* b) specifiedProfiles contains default profile - see {@link #getDefaultProfile()}
*/
boolean acceptsProfiles(String[] specifiedProfiles);
/** /**
* TODO SPR-7508: document * TODO SPR-7508: document
*/ */

View File

@ -28,10 +28,11 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.junit.matchers.JUnitMatchers.hasItem; import static org.junit.matchers.JUnitMatchers.hasItem;
import static org.junit.matchers.JUnitMatchers.hasItems; import static org.junit.matchers.JUnitMatchers.hasItems;
import static org.springframework.core.env.AbstractEnvironment.SPRING_PROFILES_PROPERTY_NAME; import static org.springframework.core.env.AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME;
import static org.springframework.core.env.AbstractEnvironment.DEFAULT_PROFILE_NAME;
import static org.springframework.core.env.AbstractEnvironment.DEFAULT_PROFILE_PROPERTY_NAME;
import static org.springframework.core.env.DefaultEnvironmentTests.CollectionMatchers.isEmpty; import static org.springframework.core.env.DefaultEnvironmentTests.CollectionMatchers.isEmpty;
import java.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.security.AccessControlException; import java.security.AccessControlException;
import java.security.Permission; import java.security.Permission;
@ -48,8 +49,6 @@ import org.hamcrest.Matcher;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.internal.matchers.TypeSafeMatcher; import org.junit.internal.matchers.TypeSafeMatcher;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
/** /**
* Unit tests for {@link DefaultEnvironment}. * Unit tests for {@link DefaultEnvironment}.
@ -220,59 +219,47 @@ public class DefaultEnvironmentTests {
public void systemPropertiesEmpty() { public void systemPropertiesEmpty() {
assertThat(environment.getActiveProfiles(), isEmpty()); assertThat(environment.getActiveProfiles(), isEmpty());
System.setProperty(SPRING_PROFILES_PROPERTY_NAME, ""); System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "");
assertThat(environment.getActiveProfiles(), isEmpty()); assertThat(environment.getActiveProfiles(), isEmpty());
System.getProperties().remove(SPRING_PROFILES_PROPERTY_NAME); System.getProperties().remove(ACTIVE_PROFILES_PROPERTY_NAME);
} }
@Test @Test
public void systemPropertiesResoloutionOfProfiles() { public void systemPropertiesResoloutionOfProfiles() {
assertThat(environment.getActiveProfiles(), isEmpty()); assertThat(environment.getActiveProfiles(), isEmpty());
System.setProperty(SPRING_PROFILES_PROPERTY_NAME, "foo"); System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "foo");
assertThat(environment.getActiveProfiles(), hasItem("foo")); assertThat(environment.getActiveProfiles(), hasItem("foo"));
// clean up // clean up
System.getProperties().remove(SPRING_PROFILES_PROPERTY_NAME); System.getProperties().remove(ACTIVE_PROFILES_PROPERTY_NAME);
} }
@Test @Test
public void systemPropertiesResoloutionOfMultipleProfiles() { public void systemPropertiesResoloutionOfMultipleProfiles() {
assertThat(environment.getActiveProfiles(), isEmpty()); assertThat(environment.getActiveProfiles(), isEmpty());
System.setProperty(SPRING_PROFILES_PROPERTY_NAME, "foo,bar"); System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "foo,bar");
assertThat(environment.getActiveProfiles(), hasItems("foo", "bar")); assertThat(environment.getActiveProfiles(), hasItems("foo", "bar"));
System.setProperty(SPRING_PROFILES_PROPERTY_NAME, " bar , baz "); // notice whitespace System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, " bar , baz "); // notice whitespace
assertThat(environment.getActiveProfiles(), not(hasItems("foo", "bar"))); assertThat(environment.getActiveProfiles(), not(hasItems("foo", "bar")));
assertThat(environment.getActiveProfiles(), hasItems("bar", "baz")); assertThat(environment.getActiveProfiles(), hasItems("bar", "baz"));
System.getProperties().remove(SPRING_PROFILES_PROPERTY_NAME); System.getProperties().remove(ACTIVE_PROFILES_PROPERTY_NAME);
} }
/*
static class WithNoProfile { }
@Profile("test")
static class WithTestProfile { }
@Test @Test
public void accepts() throws IOException { public void environmentResolutionOfDefaultSpringProfileProperty_noneSet() {
assertThat(environment.getDefaultProfile(), equalTo(DEFAULT_PROFILE_NAME));
assertThat(environment.accepts(metadataForClass(WithNoProfile.class)), is(true)); }
assertThat(environment.accepts(metadataForClass(WithTestProfile.class)), is(false));
assertThat(environment.accepts("foo,bar"), is(false)); @Test
assertThat(environment.accepts("test"), is(false)); public void environmentResolutionOfDefaultSpringProfileProperty_isSet() {
assertThat(environment.accepts("test,foo"), is(false)); testProperties.setProperty(DEFAULT_PROFILE_PROPERTY_NAME, "custom-default");
assertThat(environment.getDefaultProfile(), equalTo("custom-default"));
environment.setActiveProfiles("test");
assertThat(environment.accepts(metadataForClass(WithNoProfile.class)), is(true));
assertThat(environment.accepts(metadataForClass(WithTestProfile.class)), is(true));
assertThat(environment.accepts("foo,bar"), is(false));
assertThat(environment.accepts("test"), is(true));
assertThat(environment.accepts("test,foo"), is(true));
} }
*/
@Test @Test
public void systemPropertiesAccess() { public void systemPropertiesAccess() {
@ -413,10 +400,6 @@ public class DefaultEnvironmentTests {
} }
} }
private AnnotationMetadata metadataForClass(Class<?> clazz) throws IOException {
return new SimpleMetadataReaderFactory().getMetadataReader(clazz.getName()).getAnnotationMetadata();
}
public static class CollectionMatchers { public static class CollectionMatchers {
public static Matcher<Collection<?>> isEmpty() { public static Matcher<Collection<?>> isEmpty() {