Add @PropertySources and ignoreResourceNotFound
Support repeatable @PropertySource annotations in Java 8 and add @PropertySources container annotation for Java 6/7. Also add an ignoreResourceNotFound attribute to @PropertySource allowing missing property resources to be silently ignored. This commit also introduces some generally useful methods to AnnotationUtils for working with @Repeatable annotations. Issue: SPR-8371
This commit is contained in:
@@ -16,7 +16,9 @@
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
@@ -32,6 +34,7 @@ import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
@@ -44,6 +47,7 @@ import org.springframework.util.ClassUtils;
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
* @since 2.5
|
||||
* @see ContextAnnotationAutowireCandidateResolver
|
||||
* @see CommonAnnotationBeanPostProcessor
|
||||
@@ -297,12 +301,40 @@ public class AnnotationConfigUtils {
|
||||
return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
|
||||
}
|
||||
|
||||
static AnnotationAttributes attributesFor(AnnotatedTypeMetadata metadata, Class<?> annoClass) {
|
||||
return attributesFor(metadata, annoClass.getName());
|
||||
static AnnotationAttributes attributesFor(AnnotatedTypeMetadata metadata, Class<?> annotationClass) {
|
||||
return attributesFor(metadata, annotationClass.getName());
|
||||
}
|
||||
|
||||
static AnnotationAttributes attributesFor(AnnotatedTypeMetadata metadata, String annoClassName) {
|
||||
return AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(annoClassName, false));
|
||||
static AnnotationAttributes attributesFor(AnnotatedTypeMetadata metadata, String annotationClassName) {
|
||||
return AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(annotationClassName, false));
|
||||
}
|
||||
|
||||
static Set<AnnotationAttributes> attributesForRepeatable(AnnotationMetadata metadata,
|
||||
Class<?> containerClass, Class<?> annotationClass) {
|
||||
return attributesForRepeatable(metadata, containerClass.getName(), annotationClass.getName());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Set<AnnotationAttributes> attributesForRepeatable(AnnotationMetadata metadata,
|
||||
String containerClassName, String annotationClassName) {
|
||||
Set<AnnotationAttributes> result = new LinkedHashSet<AnnotationAttributes>();
|
||||
|
||||
addAttributesIfNotNull(result, metadata.getAnnotationAttributes(annotationClassName, false));
|
||||
|
||||
Map<String, Object> container = metadata.getAnnotationAttributes(containerClassName, false);
|
||||
if (container != null && container.containsKey("value")) {
|
||||
for (Map<String, Object> containedAttributes : (Map<String, Object>[]) container.get("value")) {
|
||||
addAttributesIfNotNull(result, containedAttributes);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableSet(result);
|
||||
}
|
||||
|
||||
private static void addAttributesIfNotNull(Set<AnnotationAttributes> result,
|
||||
Map<String, Object> attributes) {
|
||||
if (attributes != null) {
|
||||
result.add(AnnotationAttributes.fromMap(attributes));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -55,6 +56,7 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.ResourcePropertySource;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
@@ -63,6 +65,8 @@ import org.springframework.core.type.StandardAnnotationMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -112,7 +116,7 @@ class ConfigurationClassParser {
|
||||
|
||||
private final Map<String, ConfigurationClass> knownSuperclasses = new HashMap<String, ConfigurationClass>();
|
||||
|
||||
private final Stack<PropertySource<?>> propertySources = new Stack<PropertySource<?>>();
|
||||
private final MultiValueMap<String, PropertySource<?>> propertySources = new LinkedMultiValueMap<String, PropertySource<?>>();
|
||||
|
||||
private final ImportStack importStack = new ImportStack();
|
||||
|
||||
@@ -218,9 +222,9 @@ class ConfigurationClassParser {
|
||||
processMemberClasses(configClass, sourceClass);
|
||||
|
||||
// process any @PropertySource annotations
|
||||
AnnotationAttributes propertySource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(),
|
||||
org.springframework.context.annotation.PropertySource.class);
|
||||
if (propertySource != null) {
|
||||
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
|
||||
sourceClass.getMetadata(), PropertySources.class,
|
||||
org.springframework.context.annotation.PropertySource.class)) {
|
||||
processPropertySource(propertySource);
|
||||
}
|
||||
|
||||
@@ -301,29 +305,29 @@ class ConfigurationClassParser {
|
||||
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
|
||||
String name = propertySource.getString("name");
|
||||
String[] locations = propertySource.getStringArray("value");
|
||||
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
|
||||
int locationCount = locations.length;
|
||||
if (locationCount == 0) {
|
||||
throw new IllegalArgumentException("At least one @PropertySource(value) location is required");
|
||||
}
|
||||
for (int i = 0; i < locationCount; i++) {
|
||||
locations[i] = this.environment.resolveRequiredPlaceholders(locations[i]);
|
||||
}
|
||||
ClassLoader classLoader = this.resourceLoader.getClassLoader();
|
||||
if (!StringUtils.hasText(name)) {
|
||||
for (String location : locations) {
|
||||
this.propertySources.push(new ResourcePropertySource(location, classLoader));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (locationCount == 1) {
|
||||
this.propertySources.push(new ResourcePropertySource(name, locations[0], classLoader));
|
||||
}
|
||||
else {
|
||||
CompositePropertySource ps = new CompositePropertySource(name);
|
||||
for (int i = locations.length - 1; i >= 0; i--) {
|
||||
ps.addPropertySource(new ResourcePropertySource(locations[i], classLoader));
|
||||
for (String location : locations) {
|
||||
Resource resource = this.resourceLoader.getResource(
|
||||
this.environment.resolveRequiredPlaceholders(location));
|
||||
try {
|
||||
if (!StringUtils.hasText(name) || this.propertySources.containsKey(name)) {
|
||||
// We need to ensure unique names when the property source will
|
||||
// ultimately end up in a composite
|
||||
ResourcePropertySource ps = new ResourcePropertySource(resource);
|
||||
this.propertySources.add((StringUtils.hasText(name) ? name : ps.getName()), ps);
|
||||
}
|
||||
else {
|
||||
this.propertySources.add(name, new ResourcePropertySource(name, resource));
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException ex) {
|
||||
if (!ignoreResourceNotFound) {
|
||||
throw ex;
|
||||
}
|
||||
this.propertySources.push(ps);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -473,10 +477,27 @@ class ConfigurationClassParser {
|
||||
return this.configurationClasses;
|
||||
}
|
||||
|
||||
public Stack<PropertySource<?>> getPropertySources() {
|
||||
return this.propertySources;
|
||||
public List<PropertySource<?>> getPropertySources() {
|
||||
List<PropertySource<?>> propertySources = new LinkedList<PropertySource<?>>();
|
||||
for (Map.Entry<String, List<PropertySource<?>>> entry : this.propertySources.entrySet()) {
|
||||
propertySources.add(0, collatePropertySources(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
return propertySources;
|
||||
}
|
||||
|
||||
private PropertySource<?> collatePropertySources(String name,
|
||||
List<PropertySource<?>> propertySources) {
|
||||
if (propertySources.size() == 1) {
|
||||
return propertySources.get(0);
|
||||
}
|
||||
CompositePropertySource result = new CompositePropertySource(name);
|
||||
for (int i = propertySources.size() - 1; i >= 0; i--) {
|
||||
result.addPropertySource(propertySources.get(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
ImportRegistry getImportRegistry() {
|
||||
return this.importStack;
|
||||
}
|
||||
|
||||
@@ -21,13 +21,12 @@ import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
@@ -298,7 +297,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
parser.validate();
|
||||
|
||||
// Handle any @PropertySource annotations
|
||||
Stack<PropertySource<?>> parsedPropertySources = parser.getPropertySources();
|
||||
List<PropertySource<?>> parsedPropertySources = parser.getPropertySources();
|
||||
if (!parsedPropertySources.isEmpty()) {
|
||||
if (!(this.environment instanceof ConfigurableEnvironment)) {
|
||||
logger.warn("Ignoring @PropertySource annotations. " +
|
||||
@@ -306,8 +305,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
}
|
||||
else {
|
||||
MutablePropertySources envPropertySources = ((ConfigurableEnvironment)this.environment).getPropertySources();
|
||||
while (!parsedPropertySources.isEmpty()) {
|
||||
envPropertySources.addLast(parsedPropertySources.pop());
|
||||
for (PropertySource<?> propertySource : parsedPropertySources) {
|
||||
envPropertySources.addLast(propertySource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.context.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
@@ -132,7 +133,9 @@ import java.lang.annotation.Target;
|
||||
* Javadoc for details.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
* @since 3.1
|
||||
* @see PropertySources
|
||||
* @see Configuration
|
||||
* @see org.springframework.core.env.PropertySource
|
||||
* @see org.springframework.core.env.ConfigurableEnvironment#getPropertySources()
|
||||
@@ -141,6 +144,7 @@ import java.lang.annotation.Target;
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Repeatable(PropertySources.class)
|
||||
public @interface PropertySource {
|
||||
|
||||
/**
|
||||
@@ -166,4 +170,13 @@ public @interface PropertySource {
|
||||
*/
|
||||
String[] value();
|
||||
|
||||
/**
|
||||
* Indicate if failure to find the a {@link #value() property resource} should be
|
||||
* ignored.
|
||||
* <p>{@code true} is appropriate if the properties file is completely optional.
|
||||
* Default is {@code false}.
|
||||
* @since 4.0
|
||||
*/
|
||||
boolean ignoreResourceNotFound() default false;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Container annotation that aggregates several {@link PropertySource} annotations.
|
||||
*
|
||||
* <p>Can be used natively, declaring several nested {@link PropertySource} annotations.
|
||||
* Can also be used in conjunction with Java 8's support for repeatable annotations,
|
||||
* where {@link PropertySource} can simply be declared several times on the same method,
|
||||
* implicitly generating this container annotation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 4.0
|
||||
* @see PropertySource
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface PropertySources {
|
||||
|
||||
PropertySource[] value();
|
||||
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import java.lang.annotation.Target;
|
||||
* @since 3.0
|
||||
* @see EnableScheduling
|
||||
* @see ScheduledAnnotationBeanPostProcessor
|
||||
* @see Schedules
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
||||
@@ -117,17 +117,8 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
ReflectionUtils.doWithMethods(targetClass, new MethodCallback() {
|
||||
@Override
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
Schedules schedules = AnnotationUtils.getAnnotation(method, Schedules.class);
|
||||
if (schedules != null) {
|
||||
for (Scheduled scheduled : schedules.value()) {
|
||||
processScheduled(scheduled, method, bean);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Scheduled scheduled = AnnotationUtils.getAnnotation(method, Scheduled.class);
|
||||
if (scheduled != null) {
|
||||
processScheduled(scheduled, method, bean);
|
||||
}
|
||||
for (Scheduled scheduled : AnnotationUtils.getRepeatableAnnotation(method, Schedules.class, Scheduled.class)) {
|
||||
processScheduled(scheduled, method, bean);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user