M1 cut of environment, profiles and property work (SPR-7508)

Decomposed Environment interface into PropertySources, PropertyResolver
objects

    Environment interface and implementations are still present, but
    simpler.

    PropertySources container aggregates PropertySource objects;
    PropertyResolver provides search, conversion, placeholder
    replacement. Single implementation for now is
    PropertySourcesPlaceholderResolver

Renamed EnvironmentAwarePropertyPlaceholderConfigurer to
PropertySourcesPlaceholderConfigurer

    <context:property-placeholder/> now registers PSPC by default, else
    PPC if systemPropertiesMode* settings are involved

Refined configuration and behavior of default profiles

    See Environment interface Javadoc for details

Added Portlet implementations of relevant interfaces:

    * DefaultPortletEnvironment
    * PortletConfigPropertySource, PortletContextPropertySource
    * Integrated each appropriately throughout Portlet app contexts

Added protected 'createEnvironment()' method to AbstractApplicationContext

    Subclasses can override at will to supply a custom Environment
    implementation.  In practice throughout the framework, this is how
    Web- and Portlet-related ApplicationContexts override use of the
    DefaultEnvironment and swap in DefaultWebEnvironment or
    DefaultPortletEnvironment as appropriate.

Introduced "stub-and-replace" behavior for Servlet- and Portlet-based
PropertySource implementations

    Allows for early registration and ordering of the stub, then
    replacement with actual backing object at refresh() time.

    Added AbstractApplicationContext.initPropertySources() method to
    support stub-and-replace behavior. Called from within existing
    prepareRefresh() method so as to avoid impact with
    ApplicationContext implementations that copy and modify AAC's
    refresh() method (e.g.: Spring DM).

    Added methods to WebApplicationContextUtils and
    PortletApplicationContextUtils to support stub-and-replace behavior

Added comprehensive Javadoc for all new or modified types and members

Added XSD documentation for all new or modified elements and attributes

    Including nested <beans>, <beans profile="..."/>, and changes for
    certain attributes type from xsd:IDREF to xsd:string

Improved fix for detecting non-file based Resources in
PropertiesLoaderSupport (SPR-7547, SPR-7552)

    Technically unrelated to environment work, but grouped in with
    this changeset for convenience.

Deprecated (removed) context:property-placeholder
'system-properties-mode' attribute from spring-context-3.1.xsd

    Functionality is preserved for those using schemas up to and including
    spring-context-3.0.  For 3.1, system-properties-mode is no longer
    supported as it conflicts with the idea of managing a set of property
    sources within the context's Environment object. See Javadoc in
    PropertyPlaceholderConfigurer, AbstractPropertyPlaceholderConfigurer
    and PropertySourcesPlaceholderConfigurer for details.

Introduced CollectionUtils.toArray(Enumeration<E>, A[])

Work items remaining for 3.1 M2:

    Consider repackaging PropertySource* types; eliminate internal use
    of SystemPropertyUtils and deprecate

    Further work on composition of Environment interface; consider
    repurposing existing PlaceholderResolver interface to obviate need
    for resolve[Required]Placeholder() methods currently in Environment.

    Ensure configurability of placeholder prefix, suffix, and value
    separator when working against an AbstractPropertyResolver

    Add JNDI-based Environment / PropertySource implementatinos

    Consider support for @Profile at the @Bean level

    Provide consistent logging for the entire property resolution
    lifecycle; consider issuing all such messages against a dedicated
    logger with a single category.

    Add reference documentation to cover the featureset.
This commit is contained in:
Chris Beams
2011-01-03 09:04:34 +00:00
parent b130a36af7
commit b3ff9be78f
111 changed files with 3439 additions and 1715 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2010 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.
@@ -96,12 +96,13 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life
void setParent(ApplicationContext parent);
/**
* TODO SPR-7508: document
* Return the Environment for this application context in configurable form.
*/
ConfigurableEnvironment getEnvironment();
/**
* TODO SPR-7508: document
* Set the {@code Environment} for this application context.
* @param environment the new environment
*/
void setEnvironment(ConfigurableEnvironment environment);

View File

@@ -19,12 +19,17 @@ package org.springframework.context;
import org.springframework.core.env.Environment;
/**
* TODO SPR-7515: document
* Interface to be implemented by any bean that wishes to be notified
* of the {@link Environment} that it runs in.
*
* @author Chris Beams
* @since 3.1
*/
public interface EnvironmentAware {
/**
* Set the {@code Environment} that this object runs in.
*/
void setEnvironment(Environment environment);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2010 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.
@@ -50,9 +50,9 @@ public class AnnotatedBeanDefinitionReader {
/**
* Create a new AnnotatedBeanDefinitionReader for the given bean factory.
* @param registry the BeanFactory to load bean definitions into,
* in the form of a BeanDefinitionRegistry
* Create a new {@code AnnotatedBeanDefinitionReader} for the given bean factory.
* @param registry the {@code BeanFactory} to load bean definitions into,
* in the form of a {@code BeanDefinitionRegistry}
*/
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this.registry = registry;
@@ -68,8 +68,10 @@ public class AnnotatedBeanDefinitionReader {
}
/**
* Set the Environment to use when registering classes.
* Set the Environment to use when evaluating whether
* {@link Profile @Profile}-annotated component classes should be registered.
* <p>The default is a {@link DefaultEnvironment}.
* @see #registerBean(Class, String, Class...)
*/
public void setEnvironment(Environment environment) {
this.environment = environment;
@@ -80,7 +82,8 @@ public class AnnotatedBeanDefinitionReader {
* <p>The default is a {@link AnnotationBeanNameGenerator}.
*/
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
this.beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator : new AnnotationBeanNameGenerator());
this.beanNameGenerator = (beanNameGenerator != null ?
beanNameGenerator : new AnnotationBeanNameGenerator());
}
/**
@@ -88,7 +91,8 @@ public class AnnotatedBeanDefinitionReader {
* <p>The default is an {@link AnnotationScopeMetadataResolver}.
*/
public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
this.scopeMetadataResolver = (scopeMetadataResolver != null ? scopeMetadataResolver : new AnnotationScopeMetadataResolver());
this.scopeMetadataResolver = (scopeMetadataResolver != null ?
scopeMetadataResolver : new AnnotationScopeMetadataResolver());
}
@@ -110,10 +114,8 @@ public class AnnotatedBeanDefinitionReader {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
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)) {
if (Profile.Helper.isProfileAnnotationPresent(metadata)) {
if (!this.environment.acceptsProfiles(Profile.Helper.getCandidateProfiles(metadata))) {
// TODO SPR-7508: log that this bean is being rejected on profile mismatch
return;
}

View File

@@ -16,17 +16,15 @@
package org.springframework.context.annotation;
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
/**
* Standalone application context, accepting annotated classes as input - in particular
* {@link org.springframework.context.annotation.Configuration @Configuration}-annotated
* classes, but also plain {@link org.springframework.stereotype.Component @Components}
* and JSR-330 compliant classes using {@literal javax.inject} annotations. Allows for
* and JSR-330 compliant classes using {@code javax.inject} annotations. Allows for
* registering classes one by one ({@link #register}) as well as for classpath scanning
* ({@link #scan}).
*
@@ -49,15 +47,13 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex
private final ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this);
{ // TODO: rework this, it's a bit confusing
this.setEnvironment(this.getEnvironment());
}
/**
* Create a new AnnotationConfigApplicationContext that needs to be populated
* through {@link #register} calls and then manually {@link #refresh refreshed}.
* through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
*/
public AnnotationConfigApplicationContext() {
this.delegateEnvironment(super.getEnvironment());
}
/**
@@ -67,6 +63,7 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex
* e.g. {@link Configuration @Configuration} classes
*/
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses);
refresh();
}
@@ -77,16 +74,24 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex
* @param basePackages the packages to check for annotated classes
*/
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
/**
* TODO SPR-7508: document
* {@inheritDoc}
* <p>Delegates given environment to underlying {@link AnnotatedBeanDefinitionReader}
* and {@link ClassPathBeanDefinitionScanner} members.
*/
@Override
public void setEnvironment(ConfigurableEnvironment environment) {
super.setEnvironment(environment);
delegateEnvironment(environment);
}
private void delegateEnvironment(ConfigurableEnvironment environment) {
this.reader.setEnvironment(environment);
this.scanner.setEnvironment(environment);
}

View File

@@ -27,11 +27,11 @@ import org.springframework.beans.factory.annotation.Autowire;
/**
* Indicates that a method produces a bean to be managed by the Spring container. The
* names and semantics of the attributes to this annotation are intentionally similar
* to those of the {@literal <bean/>} element in the Spring XML schema.
* to those of the {@code <bean/>} element in the Spring XML schema.
*
* <p>Note that the <code>@Bean</code> annotation does not provide attributes for scope,
* primary or lazy. Rather, it should be used in conjunction with {@link Scope &#064;Scope},
* {@link Primary &#064;Primary}, and {@link Lazy &#064;Lazy} annotations to achieve
* <p>Note that the {@code @Bean} annotation does not provide attributes for scope,
* primary or lazy. Rather, it should be used in conjunction with {@link Scope @Scope},
* {@link Primary @Primary}, and {@link Lazy @Lazy} annotations to achieve
* those semantics. The same annotations can also be used at the type level, e.g. for
* component scanning.
*
@@ -96,7 +96,7 @@ public @interface Bean {
/**
* The optional name of a method to call on the bean instance upon closing the
* application context, for example a {@literal close()} method on a {@literal DataSource}.
* application context, for example a {@code close()} method on a {@code DataSource}.
* The method must have no arguments but may throw any exception.
* <p>Note: Only invoked on beans whose lifecycle is under the full control of the
* factory, which is always the case for singletons but not guaranteed

View File

@@ -116,15 +116,15 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
}
/**
* TODO SPR-7508: document
* Set the Environment to use when resolving placeholders and evaluating
* {@link Profile @Profile}-annotated component classes.
* <p>The default is a {@link DefaultEnvironment}
* @param environment the Environment to use
*/
public void setEnvironment(Environment environment) {
this.environment = environment;
}
/**
* TODO SPR-7508: document
*/
public Environment getEnvironment() {
return this.environment;
}
@@ -280,7 +280,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
* @return the pattern specification to be used for package searching
*/
protected String resolveBasePackage(String basePackage) {
return ClassUtils.convertClassNameToResourcePath(environment.resolveRequiredPlaceholders(basePackage));
return ClassUtils.convertClassNameToResourcePath(environment.getPropertyResolver().resolveRequiredPlaceholders(basePackage));
}
/**
@@ -298,12 +298,11 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
if (!metadata.hasAnnotation(Profile.class.getName())) {
if (!Profile.Helper.isProfileAnnotationPresent(metadata)) {
return true;
}
String[] specifiedProfiles =
(String[])metadata.getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME);
return this.environment.acceptsProfiles(specifiedProfiles);
// TODO SPR-7508: log that this bean is being rejected on profile mismatch
return this.environment.acceptsProfiles(Profile.Helper.getCandidateProfiles(metadata));
}
}
return false;

View File

@@ -27,7 +27,7 @@ import org.springframework.beans.factory.support.BeanNameGenerator;
/**
* Configures component scanning directives for use with {@link Configuration}
* classes. Provides support parallel with Spring XML's
* &lt;context:component-scan&gt; element.
* {@code <context:component-scan>} element.
*
* TODO SPR-7508: complete documentation.
*

View File

@@ -101,16 +101,14 @@ class ConfigurationClassParser {
}
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
if (this.environment != null && configClass.getMetadata().hasAnnotation(Profile.class.getName())) {
String[] specifiedProfiles =
(String[])configClass.getMetadata().getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME);
if (!this.environment.acceptsProfiles(specifiedProfiles)) {
AnnotationMetadata metadata = configClass.getMetadata();
if (this.environment != null && Profile.Helper.isProfileAnnotationPresent(metadata)) {
if (!this.environment.acceptsProfiles(Profile.Helper.getCandidateProfiles(metadata))) {
// TODO SPR-7508: log that this bean is being rejected on profile mismatch
return;
}
}
AnnotationMetadata metadata = configClass.getMetadata();
while (metadata != null) {
doProcessConfigurationClass(configClass, metadata);
String superClassName = metadata.getSuperClassName();

View File

@@ -49,8 +49,8 @@ import org.springframework.util.ClassUtils;
* {@link BeanFactoryPostProcessor} used for bootstrapping processing of
* {@link Configuration @Configuration} classes.
*
* <p>Registered by default when using {@literal <context:annotation-config/>} or
* {@literal <context:component-scan/>}. Otherwise, may be declared manually as
* <p>Registered by default when using {@code <context:annotation-config/>} or
* {@code <context:component-scan/>}. Otherwise, may be declared manually as
* with any other BeanFactoryPostProcessor.
*
* <p>This post processor is {@link Ordered#HIGHEST_PRECEDENCE} as it is important
@@ -100,7 +100,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
/**
* Set the {@link ProblemReporter} to use.
* <p>Used to register any problems detected with {@link Configuration} or {@link Bean}
* declarations. For instance, an @Bean method marked as {@literal final} is illegal
* declarations. For instance, an @Bean method marked as {@code final} is illegal
* and would be reported as a problem. Defaults to {@link FailFastProblemReporter}.
*/
public void setProblemReporter(ProblemReporter problemReporter) {
@@ -110,7 +110,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
/**
* Set the {@link MetadataReaderFactory} to use.
* <p>Default is a {@link CachingMetadataReaderFactory} for the specified
* {@link #setBeanClassLoader bean class loader}.
* {@linkplain #setBeanClassLoader bean class loader}.
*/
public void setMetadataReaderFactory(MetadataReaderFactory metadataReaderFactory) {
Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null");

View File

@@ -38,7 +38,7 @@ import java.lang.annotation.Documented;
* <p>Using {@link DependsOn} at the class level has no effect unless component-scanning
* is being used. If a {@link DependsOn}-annotated class is declared via XML,
* {@link DependsOn} annotation metadata is ignored, and
* {@literal <bean depends-on="..."/>} is respected instead.
* {@code <bean depends-on="..."/>} is respected instead.
*
* @author Juergen Hoeller
* @since 3.0

View File

@@ -23,18 +23,18 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates one or more {@link Configuration} classes to import.
* Indicates one or more {@link Configuration @Configuration} classes to import.
*
* <p>Provides functionality equivalent to the {@literal <import/>} element in Spring XML.
* Only supported for actual {@literal @Configuration}-annotated classes.
* <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML.
* Only supported for actual {@code @Configuration}-annotated classes.
*
* <p>{@literal @Bean} definitions declared in imported {@literal @Configuration} classes
* <p>{@code @Bean} definitions declared in imported {@code @Configuration} classes
* should be accessed by using {@link Autowired @Autowired} injection. Either the bean
* itself can be autowired, or the configuration class instance declaring the bean can be
* autowired. The latter approach allows for explicit, IDE-friendly navigation between
* {@literal @Configuration} class methods.
* {@code @Configuration} class methods.
*
* <p>If XML or other non-{@literal @Configuration} bean definition resources need to be
* <p>If XML or other non-{@code @Configuration} bean definition resources need to be
* imported, use {@link ImportResource @ImportResource}
*
* @author Chris Beams

View File

@@ -30,14 +30,14 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
* Indicates one or more resources containing bean definitions to import.
*
* <p>Like {@link Import @Import}, this annotation provides functionality similar to the
* {@literal <import/>} element in Spring XML. It is typically used when
* {@code <import/>} element in Spring XML. It is typically used when
* designing {@link Configuration @Configuration} classes to be bootstrapped by
* {@link AnnotationConfigApplicationContext}, but where some XML functionality such as
* namespaces is still necessary.
*
* <p>By default, arguments to the {@link #value()} attribute will be processed using
* an {@link XmlBeanDefinitionReader}, i.e. it is assumed that resources are Spring
* {@literal <beans/>} XML files. Optionally, the {@link #reader()} attribute may be
* {@code <beans/>} XML files. Optionally, the {@link #reader()} attribute may be
* supplied, allowing the user to specify a different {@link BeanDefinitionReader}
* implementation, such as
* {@link org.springframework.beans.factory.support.PropertiesBeanDefinitionReader}.
@@ -53,8 +53,8 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
public @interface ImportResource {
/**
* Resource paths to import. Resource-loading prefixes such as {@literal classpath:} and
* {@literal file:}, etc may be used.
* Resource paths to import. Resource-loading prefixes such as {@code classpath:} and
* {@code file:}, etc may be used.
*/
String[] value();

View File

@@ -25,25 +25,25 @@ import java.lang.annotation.Inherited;
/**
* Indicates whether a bean is to be lazily initialized.
*
*
* <p>May be used on any class directly or indirectly annotated with
* {@link org.springframework.stereotype.Component} or on methods annotated with
* {@link Bean}.
*
*
* <p>If this annotation is not present on a Component or Bean definition, eager
* initialization will occur. If present and set to {@literal true}, the
* initialization will occur. If present and set to {@code true}, the
* Bean/Component will not be initialized until referenced by another bean or
* explicitly retrieved from the enclosing
* {@link org.springframework.beans.factory.BeanFactory}. If present and set to
* {@literal false}, the bean will be instantiated on startup by bean factories
* {@code false}, the bean will be instantiated on startup by bean factories
* that perform eager initialization of singletons.
*
* <p>If Lazy is present on a {@link Configuration} class, this indicates that all
* {@link Bean} methods within that {@literal Configuration} should be lazily
* initialized. If Lazy is present and false on a Bean method within a
* Lazy-annotated Configuration class, this indicates overriding the 'default
* lazy' behavior and that the bean should be eagerly initialized.
*
*
* <p>If Lazy is present on a {@link Configuration @Configuration} class, this
* indicates that all {@link Bean @Bean} methods within that {@code @Configuration}
* should be lazily initialized. If Lazy is present and false on a Bean method
* within a Lazy-annotated Configuration class, this indicates overriding the
* 'default lazy' behavior and that the bean should be eagerly initialized.
*
* @author Chris Beams
* @since 3.0
* @see Primary

View File

@@ -27,16 +27,16 @@ import java.lang.annotation.Target;
* Indicates that a bean should be given preference when multiple candidates
* are qualified to autowire a single-valued dependency. If exactly one 'primary'
* bean exists among the candidates, it will be the autowired value.
*
*
* <p>May be used on any class directly or indirectly annotated with
* {@link org.springframework.stereotype.Component} or on methods annotated
* with {@link Bean}.
*
*
* <p>Using {@link Primary} at the class level has no effect unless component-scanning
* is being used. If a {@link Primary}-annotated class is declared via XML,
* {@link Primary} annotation metadata is ignored, and
* {@literal <bean primary="true|false"/>} is respected instead.
*
* {@code <bean primary="true|false"/>} is respected instead.
*
* @author Chris Beams
* @since 3.0
* @see Lazy

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* 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.
@@ -16,42 +16,78 @@
package org.springframework.context.annotation;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.type.AnnotationMetadata;
/**
* TODO SPR-7508: document
*
* Components not @Profile-annotated will always be registered
* ConfigurableEnvironment.setActiveProfiles(String...) sets which profiles are active
* 'spring.profile.active' sets which profiles are active (typically as a -D system property)
servlet context/init param)
* ConfigurableEnvironment.setDefaultProfiles(String...) or 'spring.profile.default' property specifies one
or more default profiles, e.g., 'default'
* if 'default' is specified as a default profile, @Profile({"xyz,default}) means that beans will be
registered if 'xyz' is active or if no profile is active
* Indicates that a component is eligible for registration when one or more {@linkplain #value
* specified profiles} are active.
*
* <p>A <em>profile</em> is a named logical grouping that may be activated programatically via
* {@link ConfigurableEnvironment#setActiveProfiles} or declaratively through setting the
* {@link AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME spring.profile.active} property,
* usually through JVM system properties, as an environment variable, or for web applications
* as a Servlet context parameter in {@code web.xml}.
*
* <p>The {@code @Profile} annotation may be used in any of the following ways:
* <ul>
* <li>as a type-level annotation on any class directly or indirectly annotated with
* {@code @Component}, including {@link Configuration @Configuration} classes
* <li>as a meta-annotation, for the purpose of composing custom stereotype annotations
* </ul>
*
* <p>If a {@code @Configuration} class is marked with {@code @Profile}, all of the
* {@code @Bean} methods and {@link Import @Import} annotations associated with that class will
* be bypassed unless one or more the specified profiles are active. This is very similar to
* the behavior in Spring XML: if the {@code profile} attribute of the {@code beans} element is
* supplied e.g., {@code <beans profile="p1,p2">}, the {@code beans} element will not be parsed unless
* profiles 'p1' and/or 'p2' have been activated. Likewise, if a {@code @Component} or
* {@code @Configuration} class is marked with <code>@Profile({"p1", "p2"})</code>, that class will
* not be registered/processed unless profiles 'p1' and/or 'p2' have been activated.
*
* <p>If the {@code @Profile} annotation is omitted, registration will occur, regardless of which,
* if any, profiles are active.
*
* <p>When defining Spring beans via XML, the {@code "profile"} attribute of the {@code <beans>}
* element may be used. See the documentation in {@code spring-beans-3.1.xsd} for details.
*
* @author Chris Beams
* @since 3.1
* @see ConfigurableEnvironment#setActiveProfiles
* @see ConfigurableEnvironment#setDefaultProfiles
* @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
* @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({
ANNOTATION_TYPE, // @Profile may be used as a meta-annotation
TYPE // In conjunction with @Component and its derivatives
})
@Target(ElementType.TYPE)
public @interface Profile {
/**
* @see #value()
*/
static final String CANDIDATE_PROFILES_ATTRIB_NAME = "value";
/**
* TODO SPR-7508: document
* The set profiles for which this component should be registered.
*/
String[] value();
static class Helper {
/**
* Return whether the given metadata includes Profile information, whether directly or
* through meta-annotation.
*/
static boolean isProfileAnnotationPresent(AnnotationMetadata metadata) {
return metadata.isAnnotated(Profile.class.getName());
}
/**
* Return the String[] of candidate profiles from {@link Profile#value()}.
*/
static String[] getCandidateProfiles(AnnotationMetadata metadata) {
return (String[])metadata.getAnnotationAttributes(Profile.class.getName()).get("value");
}
}
}

View File

@@ -35,7 +35,7 @@ import org.springframework.stereotype.Component;
* the instance returned from the method.
*
* <p>In this context, scope means the lifecycle of an instance, such as
* {@literal singleton}, {@literal prototype}, and so forth.
* {@code singleton}, {@code prototype}, and so forth.
*
* @author Mark Fisher
* @author Chris Beams
@@ -59,7 +59,7 @@ public @interface Scope {
* and if so, whether the proxy should be interface-based or subclass-based.
* <p>Defaults to {@link ScopedProxyMode#NO}, indicating that no scoped
* proxy should be created.
* <p>Analogous to {@literal <aop:scoped-proxy/>} support in Spring XML.
* <p>Analogous to {@code <aop:scoped-proxy/>} support in Spring XML.
*/
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;

View File

@@ -1,10 +1,10 @@
/**
*
*
* Annotation support for the Application Context, including JSR-250 "common"
* annotations, component-scanning, and Java-based metadata for creating
* Spring-managed objects.
*
*
*/
package org.springframework.context.annotation;

View File

@@ -20,7 +20,7 @@ import org.w3c.dom.Element;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.context.support.EnvironmentAwarePropertyPlaceholderConfigurer;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.util.StringUtils;
/**
@@ -35,32 +35,24 @@ class PropertyPlaceholderBeanDefinitionParser extends AbstractPropertyLoadingBea
@Override
protected Class<?> getBeanClass(Element element) {
// as of Spring 3.1, the default for system-properties-mode is DELEGATE,
// meaning that the attribute should be disregarded entirely, instead
// deferring to the order of PropertySource objects in the enclosing
// application context's Environment object
if (!"DELEGATE".equals(element.getAttribute("system-properties-mode"))) {
if (element.hasAttribute("system-properties-mode")) {
return PropertyPlaceholderConfigurer.class;
}
return EnvironmentAwarePropertyPlaceholderConfigurer.class;
return PropertySourcesPlaceholderConfigurer.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
super.doParse(element, builder);
builder.addPropertyValue("ignoreUnresolvablePlaceholders",
Boolean.valueOf(element.getAttribute("ignore-unresolvable")));
if (!"DELEGATE".equals(element.getAttribute("system-properties-mode"))) {
String systemPropertiesModeName = element.getAttribute("system-properties-mode");
if (StringUtils.hasLength(systemPropertiesModeName)) {
builder.addPropertyValue("systemPropertiesModeName", "SYSTEM_PROPERTIES_MODE_"+systemPropertiesModeName);
}
String systemPropertiesModeName = element.getAttribute("system-properties-mode");
if (StringUtils.hasLength(systemPropertiesModeName)) {
builder.addPropertyValue("systemPropertiesModeName", "SYSTEM_PROPERTIES_MODE_"+systemPropertiesModeName);
}
}
}

View File

@@ -44,11 +44,11 @@ public class EnvironmentAccessor implements PropertyAccessor {
}
/**
* Access provided {@literal target} object by calling its {@link Environment#getProperty(String)}
* method with the provided {@literal name}.
* Access the given target object by resolving the given property name against the given target
* environment.
*/
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(((Environment)target).getProperty(name));
return new TypedValue(((Environment)target).getPropertyResolver().getProperty(name));
}
/**

View File

@@ -206,8 +206,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
/** Statically specified listeners */
private Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<ApplicationListener<?>>();
/** TODO SPR-7508: document */
private ConfigurableEnvironment environment = new DefaultEnvironment();
/** Environment used by this context; initialized by {@link #createEnvironment()} */
private ConfigurableEnvironment environment;
/**
@@ -224,6 +224,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
public AbstractApplicationContext(ApplicationContext parent) {
this.parent = parent;
this.resourcePatternResolver = getResourcePatternResolver();
this.environment = this.createEnvironment();
}
@@ -279,6 +280,14 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
return this.environment;
}
/**
* {@inheritDoc}
* <p>Default value is determined by {@link #createEnvironment()}. Replacing the
* default with this method is one option but configuration through {@link
* #getEnvironment()} should also be considered. In either case, such modifications
* should be performed <em>before</em> {@link #refresh()}.
* @see org.springframework.context.support.AbstractApplicationContext#createEnvironment
*/
public void setEnvironment(ConfigurableEnvironment environment) {
this.environment = environment;
}
@@ -400,6 +409,12 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
return this.applicationListeners;
}
/**
* Create and return a new {@link DefaultEnvironment}.
*/
protected ConfigurableEnvironment createEnvironment() {
return new DefaultEnvironment();
}
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
@@ -456,7 +471,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
/**
* Prepare this context for refreshing, setting its startup date and
* active flag.
* active flag as well as performing any initialization of property sources.
*/
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
@@ -468,6 +483,18 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// Initialize any placeholder property sources in the context environment
initPropertySources();
}
/**
* <p>Replace any stub property sources with actual instances.
* @see org.springframework.core.env.PropertySource.StubPropertySource
* @see org.springframework.web.context.support.WebApplicationContextUtils#initSerlvetPropertySources
*/
protected void initPropertySources() {
// For subclasses: do nothing by default.
}
/**

View File

@@ -28,7 +28,7 @@ import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
/**
* Base class for {@link org.springframework.context.ApplicationContext}
* implementations which are supposed to support multiple calls to {@literal refresh},
* implementations which are supposed to support multiple calls to {@link #refresh()},
* creating a new internal bean factory instance every time.
* Typically (but not necessarily), such a context will be driven by
* a set of config locations to load bean definitions from.
@@ -50,7 +50,7 @@ import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
* and {@link FileSystemXmlApplicationContext}, which both derive from the
* common {@link AbstractXmlApplicationContext} base class;
* {@link org.springframework.context.annotation.AnnotationConfigApplicationContext}
* supports {@literal @Configuration}-annotated classes as a source of bean definitions.
* supports {@code @Configuration}-annotated classes as a source of bean definitions.
*
* @author Juergen Hoeller
* @author Chris Beams
@@ -182,7 +182,7 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
* Called for each {@link #refresh()} attempt.
* <p>The default implementation creates a
* {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
* with the {@link #getInternalParentBeanFactory() internal bean factory} of this
* with the {@linkplain #getInternalParentBeanFactory() internal bean factory} of this
* context's parent as parent bean factory. Can be overridden in subclasses,
* for example to customize DefaultListableBeanFactory's settings.
* @return the bean factory for this context
@@ -199,8 +199,8 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
* Customize the internal bean factory used by this context.
* Called for each {@link #refresh()} attempt.
* <p>The default implementation applies this context's
* {@link #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
* and {@link #setAllowCircularReferences "allowCircularReferences"} settings,
* {@linkplain #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
* and {@linkplain #setAllowCircularReferences "allowCircularReferences"} settings,
* if specified. Can be overridden in subclasses to customize any of
* {@link DefaultListableBeanFactory}'s settings.
* @param beanFactory the newly created bean factory for this context

View File

@@ -17,11 +17,11 @@
package org.springframework.context.support;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.SystemPropertyUtils;
/**
* {@link AbstractRefreshableApplicationContext} subclass that adds common handling
@@ -117,14 +117,9 @@ public abstract class AbstractRefreshableConfigApplicationContext extends Abstra
* system property values if necessary. Applied to config locations.
* @param path the original file path
* @return the resolved file path
* @see SystemPropertyUtils#resolveRequiredPlaceholders(String)
*/
protected String resolvePath(String path) {
// TODO SPR-7508: note that ARAC cannot delegate to its beanFactory's environment
// to call Environment.resolve[Required]Placeholders(String), as the bean factory
// has not yet been initialized. This amounts to one more reason not to use the ARAC
// hierarchy - it won't have early access to environment property resolution.
return SystemPropertyUtils.resolvePlaceholders(path);
return this.getEnvironment().getPropertyResolver().resolveRequiredPlaceholders(path);
}

View File

@@ -1,115 +0,0 @@
/*
* Copyright 2002-2010 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.context.support;
import java.util.LinkedList;
import java.util.Properties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.AbstractPropertyPlaceholderConfigurer;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.util.Assert;
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
/**
* TODO SPR-7508: document
*
* Local properties are added as a property source in any case. Precedence is based
* on the value of the {@link #setLocalOverride(boolean) localOverride} property.
*
* @author Chris Beams
* @since 3.1
* @see PropertyPlaceholderConfigurer
* @see EnvironmentAwarePropertyOverrideConfigurer
*/
public class EnvironmentAwarePropertyPlaceholderConfigurer
extends AbstractPropertyPlaceholderConfigurer implements EnvironmentAware {
private ConfigurableEnvironment environment;
private Environment wrappedEnvironment;
public void setEnvironment(Environment environment) {
this.wrappedEnvironment = environment;
}
@Override
protected PlaceholderResolver getPlaceholderResolver(Properties props) {
return new PlaceholderResolver() {
public String resolvePlaceholder(String placeholderName) {
return environment.getProperty(placeholderName);
}
};
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Assert.notNull(this.wrappedEnvironment, "Environment must not be null. Did you call setEnvironment()?");
environment = new AbstractEnvironment() { };
LinkedList<PropertySource<?>> propertySources = environment.getPropertySources();
EnvironmentPropertySource environmentPropertySource =
new EnvironmentPropertySource("wrappedEnvironment", wrappedEnvironment);
if (!this.localOverride) {
propertySources.add(environmentPropertySource);
}
if (this.localProperties != null) {
int cx=0;
for (Properties localProps : this.localProperties) {
propertySources.add(new PropertiesPropertySource("localProperties"+cx++, localProps));
}
}
if (this.localOverride) {
propertySources.add(environmentPropertySource);
}
super.postProcessBeanFactory(beanFactory);
}
static class EnvironmentPropertySource extends PropertySource<Environment> {
public EnvironmentPropertySource(String name, Environment source) {
super(name, source);
}
@Override
public boolean containsProperty(String key) {
return source.containsProperty(key);
}
@Override
public String getProperty(String key) {
return source.getProperty(key);
}
@Override
public int size() {
// TODO Auto-generated method stub
return source.getPropertyCount();
}
}
}

View File

@@ -28,7 +28,6 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;

View File

@@ -44,8 +44,8 @@ public class GenericXmlApplicationContext extends GenericApplicationContext {
/**
* Create a new GenericXmlApplicationContext that needs to be populated
* through {@link #load} calls and then manually {@link #refresh refreshed}.
* Create a new GenericXmlApplicationContext that needs to be
* {@linkplain #load loaded} and then manually {@link #refresh refreshed}.
*/
public GenericXmlApplicationContext() {
reader.setEnvironment(this.getEnvironment());
@@ -91,8 +91,9 @@ public class GenericXmlApplicationContext extends GenericApplicationContext {
}
/**
* Set a custom environment. Should be called before any call to
* {@link #load}. TODO SPR-7508: document
* {@inheritDoc}
* <p>Delegates the given environment to underlying {@link XmlBeanDefinitionReader}.
* Should be called before any call to {@link #load}.
*/
@Override
public void setEnvironment(ConfigurableEnvironment environment) {

View File

@@ -0,0 +1,134 @@
/*
* 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.context.support;
import java.io.IOException;
import java.util.Properties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.config.AbstractPropertyPlaceholderConfigurer;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertyResolver;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.PropertySources;
import org.springframework.core.env.PropertySourcesPropertyResolver;
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
/**
* Specialization of {@link AbstractPropertyPlaceholderConfigurer}
*
* <p>Local properties are added as a property source in any case. Precedence is based
* on the value of the {@link #setLocalOverride localOverride} property.
*
* @author Chris Beams
* @since 3.1
* @see AbstractPropertyPlaceholderConfigurer
* @see PropertyPlaceholderConfigurer
*/
public class PropertySourcesPlaceholderConfigurer extends AbstractPropertyPlaceholderConfigurer
implements EnvironmentAware {
/**
* {@value} is the name given to the {@link PropertySource} for the set of
* {@linkplain #mergeProperties() merged properties} supplied to this configurer.
*/
public static final String LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME = "localProperties";
private MutablePropertySources propertySources;
private PropertyResolver propertyResolver;
private Environment environment;
/**
* {@inheritDoc}
* <p>{@code PropertySources} from this environment will be searched when replacing ${...} placeholders
* @see #setPropertySources
* @see #postProcessBeanFactory
*/
public void setEnvironment(Environment environment) {
this.environment = environment;
}
/**
* Customize the set of {@link PropertySources} to be used by this configurer.
* Setting this property indicates that environment property sources and local
* properties should be ignored.
* @see #postProcessBeanFactory
*/
public void setPropertySources(PropertySources propertySources) {
this.propertySources = new MutablePropertySources(propertySources);
}
@Override
protected PlaceholderResolver getPlaceholderResolver(Properties props) {
return new PlaceholderResolver() {
public String resolvePlaceholder(String placeholderName) {
return propertyResolver.getProperty(placeholderName);
}
};
}
/**
* {@inheritDoc}
* <p>Processing occurs by replacing ${...} placeholders in bean definitions by resolving each
* against this configurer's set of {@link PropertySources}, which includes:
* <ul>
* <li>all {@linkplain Environment#getPropertySources environment property sources}, if an
* {@code Environment} {@linkplain #setEnvironment is present}
* <li>{@linkplain #mergeProperties merged local properties}, if {@linkplain #setLocation any}
* {@linkplain #setLocations have} {@linkplain #setProperties been}
* {@linkplain #setPropertiesArray specified}
* <li>any property sources set by calling {@link #setPropertySources}
* </ul>
* <p>If {@link #setPropertySources} is called, <strong>environment and local properties will be
* ignored</strong>. This method is designed to give the user fine-grained control over property
* sources, and once set, the configurer makes no assumptions about adding additional sources.
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.propertySources == null) {
this.propertySources = new MutablePropertySources();
if (this.environment != null) {
this.propertySources.addAll(this.environment.getPropertySources());
}
try {
PropertySource<?> localPropertySource =
new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, this.mergeProperties());
if (this.localOverride) {
this.propertySources.addFirst(localPropertySource);
} else {
this.propertySources.addLast(localPropertySource);
}
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}
this.propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
this.processProperties(beanFactory, this.propertyResolver.asProperties());
}
}

View File

@@ -30,8 +30,6 @@ import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.DefaultEnvironment;
import org.springframework.jndi.JndiLocatorSupport;
import org.springframework.jndi.TypeMismatchNamingException;
@@ -70,9 +68,6 @@ public class SimpleJndiBeanFactory extends JndiLocatorSupport implements BeanFac
/** Cache of the types of nonshareable resources: bean name --> bean type */
private final Map<String, Class> resourceTypes = new HashMap<String, Class>();
/** TODO SPR-7508: should be JNDI-specific environment */
private ConfigurableEnvironment environment = new DefaultEnvironment();
public SimpleJndiBeanFactory() {
setResourceRef(true);

View File

@@ -90,42 +90,17 @@
<xsd:annotation>
<xsd:documentation><![CDATA[
Activates replacement of ${...} placeholders, resolved against the specified properties file or
Properties object (if any). Defines an EnvironmentAwarePropertyPlaceholderConfigurer within the context.
Properties object (if any). Defines an PropertySourcesPlaceholderConfigurer within the context.
For backward compatibility with versions earlier than Spring 3.1, a PropertyPlaceholderConfigurer will be
registered if the 'system-properties-mode' attribute has been explicitly assigned a value.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports
type="org.springframework.beans.factory.config.AbstractPropertyPlaceholderConfigurer" />
type="org.springframework.context.support.PropertySourcesPlaceholderConfigurer" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="propertyPlaceholder">
<xsd:attribute name="system-properties-mode" default="DELEGATE">
<xsd:annotation>
<xsd:documentation><![CDATA[
If set to any value other than DELEGATE, register a PropertyPlaceholderConfigurer bean and call
its setSystemPropertiesModeName() method with the value. If set to DELEGATE (the default), register
an EnvironmentAwarePropertyPlaceholderConfigurer and ignore the setting altogether, instead delegating
to the enclosing application context's Environment object and its collection of PropertySource objects
which may or may not include system properties and environment variables.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="DELEGATE"/>
<xsd:enumeration value="NEVER"/>
<xsd:enumeration value="FALLBACK"/>
<xsd:enumeration value="OVERRIDE"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="property-override">

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2002-2010 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 example.profilescan;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Profile(DevComponent.PROFILE_NAME)
@Component
public @interface DevComponent {
public static final String PROFILE_NAME = "dev";
String value() default "";
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2002-2010 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 example.profilescan;
@DevComponent(ProfileMetaAnnotatedComponent.BEAN_NAME)
public class ProfileMetaAnnotatedComponent {
public static final String BEAN_NAME = "profileMetaAnnotatedComponent";
}

View File

@@ -39,7 +39,9 @@ import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import example.profilescan.DevComponent;
import example.profilescan.ProfileAnnotatedComponent;
import example.profilescan.ProfileMetaAnnotatedComponent;
import example.scannable.FooDao;
import example.scannable.FooService;
import example.scannable.FooServiceImpl;
@@ -207,6 +209,15 @@ public class ClassPathScanningCandidateComponentProviderTests {
assertThat(ctx.containsBean(ProfileAnnotatedComponent.BEAN_NAME), is(true));
}
@Test
public void testIntegrationWithAnnotationConfigApplicationContext_validMetaAnnotatedProfile() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles(DevComponent.PROFILE_NAME);
ctx.register(ProfileMetaAnnotatedComponent.class);
ctx.refresh();
assertThat(ctx.containsBean(ProfileMetaAnnotatedComponent.BEAN_NAME), is(true));
}
@Test
public void testIntegrationWithAnnotationConfigApplicationContext_invalidProfile() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
@@ -216,6 +227,15 @@ public class ClassPathScanningCandidateComponentProviderTests {
assertThat(ctx.containsBean(ProfileAnnotatedComponent.BEAN_NAME), is(false));
}
@Test
public void testIntegrationWithAnnotationConfigApplicationContext_invalidMetaAnnotatedProfile() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("other");
ctx.register(ProfileMetaAnnotatedComponent.class);
ctx.refresh();
assertThat(ctx.containsBean(ProfileMetaAnnotatedComponent.BEAN_NAME), is(false));
}
@Test
public void testIntegrationWithAnnotationConfigApplicationContext_defaultProfile() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();

View File

@@ -40,7 +40,7 @@ import test.beans.TestBean;
* particularly convenient syntax requiring no extra artifact for the aspect.
*
* <p>Currently it is assumed that the user is bootstrapping Configuration class processing via XML (using
* annotation-config or component-scan), and thus will also use {@literal <aop:aspectj-autoproxy/>} to enable
* annotation-config or component-scan), and thus will also use {@code <aop:aspectj-autoproxy/>} to enable
* processing of the Aspect annotation.
*
* @author Chris Beams

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* 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.
@@ -72,7 +72,7 @@ public class ContextNamespaceHandlerTests {
@Test
public void propertyPlaceholderEnvironmentProperties() throws Exception {
MockEnvironment env = MockEnvironment.withProperty("foo", "spam");
MockEnvironment env = new MockEnvironment().withProperty("foo", "spam");
GenericXmlApplicationContext applicationContext = new GenericXmlApplicationContext();
applicationContext.setEnvironment(env);
applicationContext.load(new ClassPathResource("contextNamespaceHandlerTests-simple.xml", getClass()));

View File

@@ -20,12 +20,11 @@ import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition;
import java.util.HashMap;
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.mock.env.MockPropertySource;
import test.beans.TestBean;
@@ -49,7 +48,7 @@ public class EnvironmentAccessorIntegrationTests {
.getBeanDefinition());
GenericApplicationContext ctx = new GenericApplicationContext(bf);
ctx.getEnvironment().addPropertySource("testMap", new HashMap() {{ put("my.name", "myBean"); }});
ctx.getEnvironment().getPropertySources().addFirst(new MockPropertySource().withProperty("my.name", "myBean"));
ctx.refresh();
assertThat(ctx.getBean(TestBean.class).getName(), equalTo("myBean"));

View File

@@ -1,91 +0,0 @@
/*
* Copyright 2002-2010 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.context.support;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition;
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.mock.env.MockEnvironment;
import test.beans.TestBean;
/**
* Unit tests for {@link EnvironmentAwarePropertyPlaceholderConfigurer}.
*
* @author Chris Beams
* @since 3.1
* @see EnvironmentAwarePropertyPlaceholderConfigurerTests
*/
public class EnvironmentAwarePropertyPlaceholderConfigurerTests {
@Test(expected=IllegalArgumentException.class)
public void environmentNotNull() {
new EnvironmentAwarePropertyPlaceholderConfigurer().postProcessBeanFactory(new DefaultListableBeanFactory());
}
@Test
public void localPropertiesOverrideFalse() {
localPropertiesOverride(false);
}
@Test
public void localPropertiesOverrideTrue() {
localPropertiesOverride(true);
}
private void localPropertiesOverride(boolean override) {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean",
genericBeanDefinition(TestBean.class)
.addPropertyValue("name", "${foo}")
.getBeanDefinition());
EnvironmentAwarePropertyPlaceholderConfigurer ppc = new EnvironmentAwarePropertyPlaceholderConfigurer();
ppc.setLocalOverride(override);
ppc.setProperties(MockEnvironment.withProperty("foo", "local").asProperties());
ppc.setEnvironment(MockEnvironment.withProperty("foo", "enclosing"));
ppc.postProcessBeanFactory(bf);
if (override) {
assertThat(bf.getBean(TestBean.class).getName(), equalTo("local"));
} else {
assertThat(bf.getBean(TestBean.class).getName(), equalTo("enclosing"));
}
}
@Test
public void simpleReplacement() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean",
genericBeanDefinition(TestBean.class)
.addPropertyValue("name", "${my.name}")
.getBeanDefinition());
MockEnvironment env = new MockEnvironment();
env.setProperty("my.name", "myValue");
EnvironmentAwarePropertyPlaceholderConfigurer ppc =
new EnvironmentAwarePropertyPlaceholderConfigurer();
ppc.setEnvironment(env);
ppc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName(), equalTo("myValue"));
}
}

View File

@@ -9,7 +9,7 @@ import org.springframework.util.ClassUtils;
/**
* Unit tests for {@link GenericXmlApplicationContext}.
*
*
* See SPR-7530.
*
* @author Chris Beams

View File

@@ -0,0 +1,163 @@
/*
* Copyright 2002-2010 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.context.support;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition;
import java.util.Properties;
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.mock.env.MockPropertySource;
import test.beans.TestBean;
/**
* Unit tests for {@link PropertySourcesPlaceholderConfigurer}.
*
* @author Chris Beams
* @since 3.1
*/
public class PropertySourcesPlaceholderConfigurerTests {
@Test
public void replacementFromEnvironmentProperties() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean",
genericBeanDefinition(TestBean.class)
.addPropertyValue("name", "${my.name}")
.getBeanDefinition());
MockEnvironment env = new MockEnvironment();
env.setProperty("my.name", "myValue");
PropertySourcesPlaceholderConfigurer ppc =
new PropertySourcesPlaceholderConfigurer();
ppc.setEnvironment(env);
ppc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName(), equalTo("myValue"));
}
@Test
public void localPropertiesViaResource() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean",
genericBeanDefinition(TestBean.class)
.addPropertyValue("name", "${my.name}")
.getBeanDefinition());
PropertySourcesPlaceholderConfigurer pc = new PropertySourcesPlaceholderConfigurer();
Resource resource = new ClassPathResource("PropertySourcesPlaceholderConfigurerTests.properties", this.getClass());
pc.setLocation(resource);
pc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName(), equalTo("foo"));
}
@Test
public void localPropertiesOverrideFalse() {
localPropertiesOverride(false);
}
@Test
public void localPropertiesOverrideTrue() {
localPropertiesOverride(true);
}
@Test
public void explicitPropertySources() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean",
genericBeanDefinition(TestBean.class)
.addPropertyValue("name", "${my.name}")
.getBeanDefinition());
MutablePropertySources propertySources = new MutablePropertySources();
propertySources.addLast(new MockPropertySource().withProperty("my.name", "foo"));
PropertySourcesPlaceholderConfigurer pc = new PropertySourcesPlaceholderConfigurer();
pc.setPropertySources(propertySources);
pc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName(), equalTo("foo"));
}
@Test
public void explicitPropertySourcesExcludesEnvironment() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean",
genericBeanDefinition(TestBean.class)
.addPropertyValue("name", "${my.name}")
.getBeanDefinition());
MutablePropertySources propertySources = new MutablePropertySources();
propertySources.addLast(new MockPropertySource());
PropertySourcesPlaceholderConfigurer pc = new PropertySourcesPlaceholderConfigurer();
pc.setPropertySources(propertySources);
pc.setEnvironment(new MockEnvironment().withProperty("my.name", "env"));
pc.setIgnoreUnresolvablePlaceholders(true);
pc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName(), equalTo("${my.name}"));
}
@Test
@SuppressWarnings("serial")
public void explicitPropertySourcesExcludesLocalProperties() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean",
genericBeanDefinition(TestBean.class)
.addPropertyValue("name", "${my.name}")
.getBeanDefinition());
MutablePropertySources propertySources = new MutablePropertySources();
propertySources.addLast(new MockPropertySource());
PropertySourcesPlaceholderConfigurer pc = new PropertySourcesPlaceholderConfigurer();
pc.setPropertySources(propertySources);
pc.setProperties(new Properties() {{ put("my.name", "local"); }});
pc.setIgnoreUnresolvablePlaceholders(true);
pc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName(), equalTo("${my.name}"));
}
@SuppressWarnings("serial")
private void localPropertiesOverride(boolean override) {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean",
genericBeanDefinition(TestBean.class)
.addPropertyValue("name", "${foo}")
.getBeanDefinition());
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setLocalOverride(override);
ppc.setProperties(new Properties() {{ setProperty("foo", "local"); }});
ppc.setEnvironment(new MockEnvironment().withProperty("foo", "enclosing"));
ppc.postProcessBeanFactory(bf);
if (override) {
assertThat(bf.getBean(TestBean.class).getName(), equalTo("local"));
} else {
assertThat(bf.getBean(TestBean.class).getName(), equalTo("enclosing"));
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* 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.
@@ -19,30 +19,41 @@ package org.springframework.mock.env;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* Simple {@link ConfigurableEnvironment} implementation exposing a
* {@link #setProperty(String, String)} and {@link #withProperty(String, String)}
* methods for testing purposes.
*
* @author Chris Beams
* @since 3.1
* @see MockPropertySource
*/
public class MockEnvironment extends AbstractEnvironment {
private MockPropertySource propertySource = new MockPropertySource();
/**
* Create a new {@code MockEnvironment} with a single {@link MockPropertySource}.
*/
public MockEnvironment() {
getPropertySources().add(propertySource);
getPropertySources().addLast(propertySource);
}
/**
* Set a property on the underlying {@link MockPropertySource} for this environment.
*/
public void setProperty(String key, String value) {
propertySource.setProperty(key, value);
}
public static MockEnvironment withProperty(String key, String value) {
MockEnvironment environment = new MockEnvironment();
environment.setProperty(key, value);
return environment;
/**
* Convenient synonym for {@link #setProperty} that returns the current instance.
* Useful for method chaining and fluent-style use.
* @return this {@link MockEnvironment} instance
* @see MockPropertySource#withProperty(String, String)
*/
public MockEnvironment withProperty(String key, String value) {
this.setProperty(key, value);
return this;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* 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.
@@ -19,24 +19,85 @@ package org.springframework.mock.env;
import java.util.Properties;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
/**
* Simple {@link PropertySource} implementation for use in testing. Accepts
* a user-provided {@link Properties} object, or if omitted during construction,
* the implementation will initialize its own.
*
* The {@link #setProperty} and {@link #withProperty} methods are exposed for
* convenience, for example:
* <pre>
* {@code
* PropertySource<?> source = new MockPropertySource().withProperty("foo", "bar");
* }
* </pre>
*
* @author Chris Beams
* @since 3.1
* @see MockEnvironment
*/
public class MockPropertySource extends PropertiesPropertySource {
/**
* {@value} is the default name for {@link MockPropertySource} instances not
* otherwise given an explicit name.
* @see #MockPropertySource()
* @see #MockPropertySource(String)
*/
public static final String MOCK_PROPERTIES_PROPERTY_SOURCE_NAME = "mockProperties";
/**
* Create a new {@code MockPropertySource} named {@value #MOCK_PROPERTIES_PROPERTY_SOURCE_NAME}
* that will maintain its own internal {@link Properties} instance.
*/
public MockPropertySource() {
this(new Properties());
}
private MockPropertySource(Properties properties) {
super("mockProperties", properties);
/**
* Create a new {@code MockPropertySource} with the given name that will
* maintain its own internal {@link Properties} instance.
* @param name the {@linkplain #getName() name} of the property source
*/
public MockPropertySource(String name) {
this(name, new Properties());
}
/**
* Create a new {@code MockPropertySource} named {@value #MOCK_PROPERTIES_PROPERTY_SOURCE_NAME}
* and backed by the given {@link Properties} object.
* @param properties the properties to use
*/
public MockPropertySource(Properties properties) {
this(MOCK_PROPERTIES_PROPERTY_SOURCE_NAME, properties);
}
/**
* Create a new {@code MockPropertySource} with with the given name and backed by the given
* {@link Properties} object
* @param name the {@linkplain #getName() name} of the property source
* @param properties the properties to use
*/
public MockPropertySource(String name, Properties properties) {
super(name, properties);
}
/**
* Set the given property on the underlying {@link Properties} object.
*/
public void setProperty(String key, String value) {
this.source.setProperty(key, value);
this.source.put(key, value);
}
public static MockPropertySource withProperty(String key, String value) {
Properties properties = new Properties();
properties.setProperty(key, value);
return new MockPropertySource(properties);
/**
* Convenient synonym for {@link #setProperty} that returns the current instance.
* Useful for method chaining and fluent-style use.
* @return this {@link MockPropertySource} instance
*/
public MockPropertySource withProperty(String key, String value) {
this.setProperty(key, value);
return this;
}
}

View File

@@ -3,7 +3,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
<util:properties id="placeholderProps">