Support for @PropertySource annotations with custom implementation types
Issue: SPR-8963
This commit is contained in:
parent
8ff9e818a5
commit
a3789120c9
|
|
@ -66,7 +66,9 @@ import org.springframework.core.env.MutablePropertySources;
|
||||||
import org.springframework.core.env.PropertySource;
|
import org.springframework.core.env.PropertySource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
|
import org.springframework.core.io.support.DefaultPropertySourceFactory;
|
||||||
import org.springframework.core.io.support.EncodedResource;
|
import org.springframework.core.io.support.EncodedResource;
|
||||||
|
import org.springframework.core.io.support.PropertySourceFactory;
|
||||||
import org.springframework.core.io.support.ResourcePropertySource;
|
import org.springframework.core.io.support.ResourcePropertySource;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
import org.springframework.core.type.MethodMetadata;
|
import org.springframework.core.type.MethodMetadata;
|
||||||
|
|
@ -103,6 +105,8 @@ import org.springframework.util.StringUtils;
|
||||||
*/
|
*/
|
||||||
class ConfigurationClassParser {
|
class ConfigurationClassParser {
|
||||||
|
|
||||||
|
private static final PropertySourceFactory DEFAULT_PROPERTY_SOURCE_FACTORY = new DefaultPropertySourceFactory();
|
||||||
|
|
||||||
private static final Comparator<DeferredImportSelectorHolder> DEFERRED_IMPORT_COMPARATOR =
|
private static final Comparator<DeferredImportSelectorHolder> DEFERRED_IMPORT_COMPARATOR =
|
||||||
new Comparator<ConfigurationClassParser.DeferredImportSelectorHolder>() {
|
new Comparator<ConfigurationClassParser.DeferredImportSelectorHolder>() {
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -355,15 +359,26 @@ class ConfigurationClassParser {
|
||||||
*/
|
*/
|
||||||
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
|
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
|
||||||
String name = propertySource.getString("name");
|
String name = propertySource.getString("name");
|
||||||
|
if (!StringUtils.hasLength(name)) {
|
||||||
|
name = null;
|
||||||
|
}
|
||||||
String encoding = propertySource.getString("encoding");
|
String encoding = propertySource.getString("encoding");
|
||||||
|
if (!StringUtils.hasLength(encoding)) {
|
||||||
|
encoding = null;
|
||||||
|
}
|
||||||
String[] locations = propertySource.getStringArray("value");
|
String[] locations = propertySource.getStringArray("value");
|
||||||
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
|
|
||||||
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
|
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
|
||||||
|
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
|
||||||
|
|
||||||
|
Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
|
||||||
|
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
|
||||||
|
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiate(factoryClass));
|
||||||
|
|
||||||
for (String location : locations) {
|
for (String location : locations) {
|
||||||
try {
|
try {
|
||||||
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
|
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
|
||||||
Resource resource = this.resourceLoader.getResource(resolvedLocation);
|
Resource resource = this.resourceLoader.getResource(resolvedLocation);
|
||||||
addPropertySource(createPropertySource(name, encoding, resource));
|
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException ex) {
|
catch (IllegalArgumentException ex) {
|
||||||
// from resolveRequiredPlaceholders
|
// from resolveRequiredPlaceholders
|
||||||
|
|
@ -380,34 +395,23 @@ class ConfigurationClassParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResourcePropertySource createPropertySource(String name, String encoding, Resource resource) throws IOException {
|
private void addPropertySource(PropertySource<?> propertySource) {
|
||||||
if (StringUtils.hasText(name)) {
|
|
||||||
return (StringUtils.hasText(encoding) ?
|
|
||||||
new ResourcePropertySource(name, new EncodedResource(resource, encoding)) :
|
|
||||||
new ResourcePropertySource(name, resource));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return (StringUtils.hasText(encoding) ?
|
|
||||||
new ResourcePropertySource(new EncodedResource(resource, encoding)) :
|
|
||||||
new ResourcePropertySource(resource));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addPropertySource(ResourcePropertySource propertySource) {
|
|
||||||
String name = propertySource.getName();
|
String name = propertySource.getName();
|
||||||
MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
|
MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
|
||||||
if (propertySources.contains(name) && this.propertySourceNames.contains(name)) {
|
if (propertySources.contains(name) && this.propertySourceNames.contains(name)) {
|
||||||
// We've already added a version, we need to extend it
|
// We've already added a version, we need to extend it
|
||||||
PropertySource<?> existing = propertySources.get(name);
|
PropertySource<?> existing = propertySources.get(name);
|
||||||
|
PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
|
||||||
|
((ResourcePropertySource) propertySource).withResourceName() : propertySource);
|
||||||
if (existing instanceof CompositePropertySource) {
|
if (existing instanceof CompositePropertySource) {
|
||||||
((CompositePropertySource) existing).addFirstPropertySource(propertySource.withResourceName());
|
((CompositePropertySource) existing).addFirstPropertySource(newSource);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (existing instanceof ResourcePropertySource) {
|
if (existing instanceof ResourcePropertySource) {
|
||||||
existing = ((ResourcePropertySource) existing).withResourceName();
|
existing = ((ResourcePropertySource) existing).withResourceName();
|
||||||
}
|
}
|
||||||
CompositePropertySource composite = new CompositePropertySource(name);
|
CompositePropertySource composite = new CompositePropertySource(name);
|
||||||
composite.addPropertySource(propertySource.withResourceName());
|
composite.addPropertySource(newSource);
|
||||||
composite.addPropertySource(existing);
|
composite.addPropertySource(existing);
|
||||||
propertySources.replace(name, composite);
|
propertySources.replace(name, composite);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import org.springframework.core.io.support.PropertySourceFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Annotation providing a convenient and declarative mechanism for adding a
|
* Annotation providing a convenient and declarative mechanism for adding a
|
||||||
* {@link org.springframework.core.env.PropertySource PropertySource} to Spring's
|
* {@link org.springframework.core.env.PropertySource PropertySource} to Spring's
|
||||||
|
|
@ -133,6 +135,7 @@ import java.lang.annotation.Target;
|
||||||
* javadocs for details.
|
* javadocs for details.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
* @see PropertySources
|
* @see PropertySources
|
||||||
|
|
@ -155,12 +158,6 @@ public @interface PropertySource {
|
||||||
*/
|
*/
|
||||||
String name() default "";
|
String name() default "";
|
||||||
|
|
||||||
/**
|
|
||||||
* A specific character encoding for the given resources, e.g. "UTF-8".
|
|
||||||
* @since 4.3
|
|
||||||
*/
|
|
||||||
String encoding() default "";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicate the resource location(s) of the properties file to be loaded.
|
* Indicate the resource location(s) of the properties file to be loaded.
|
||||||
* For example, {@code "classpath:/com/myco/app.properties"} or
|
* For example, {@code "classpath:/com/myco/app.properties"} or
|
||||||
|
|
@ -184,4 +181,19 @@ public @interface PropertySource {
|
||||||
*/
|
*/
|
||||||
boolean ignoreResourceNotFound() default false;
|
boolean ignoreResourceNotFound() default false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A specific character encoding for the given resources, e.g. "UTF-8".
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
String encoding() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify a custom {@link PropertySourceFactory}, if any.
|
||||||
|
* <p>By default, a default factory for standard resource files will be used.
|
||||||
|
* @since 4.3
|
||||||
|
* @see org.springframework.core.io.support.DefaultPropertySourceFactory
|
||||||
|
* @see org.springframework.core.io.support.ResourcePropertySource
|
||||||
|
*/
|
||||||
|
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -17,8 +17,12 @@
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.Properties;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
|
@ -27,9 +31,13 @@ import org.junit.rules.ExpectedException;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
|
import org.springframework.core.annotation.AliasFor;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.core.env.MapPropertySource;
|
import org.springframework.core.env.MapPropertySource;
|
||||||
import org.springframework.core.env.MutablePropertySources;
|
import org.springframework.core.env.MutablePropertySources;
|
||||||
|
import org.springframework.core.io.support.EncodedResource;
|
||||||
|
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||||
|
import org.springframework.core.io.support.PropertySourceFactory;
|
||||||
import org.springframework.tests.sample.beans.TestBean;
|
import org.springframework.tests.sample.beans.TestBean;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.*;
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
|
|
@ -111,6 +119,22 @@ public class PropertySourceAnnotationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void withCustomFactory() {
|
||||||
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||||
|
ctx.register(ConfigWithImplicitName.class, WithCustomFactory.class);
|
||||||
|
ctx.refresh();
|
||||||
|
assertThat(ctx.getBean(TestBean.class).getName(), equalTo("P2TESTBEAN"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void withCustomFactoryAsMeta() {
|
||||||
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||||
|
ctx.register(ConfigWithImplicitName.class, WithCustomFactoryAsMeta.class);
|
||||||
|
ctx.refresh();
|
||||||
|
assertThat(ctx.getBean(TestBean.class).getName(), equalTo("P2TESTBEAN"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void withUnresolvablePlaceholder() {
|
public void withUnresolvablePlaceholder() {
|
||||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||||
|
|
@ -354,6 +378,43 @@ public class PropertySourceAnnotationTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@PropertySource(value = "classpath:org/springframework/context/annotation/p2.properties", factory = MyCustomFactory.class)
|
||||||
|
static class WithCustomFactory {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@MyPropertySource(value = "classpath:org/springframework/context/annotation/p2.properties")
|
||||||
|
static class WithCustomFactoryAsMeta {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@PropertySource(value = {}, factory = MyCustomFactory.class)
|
||||||
|
public @interface MyPropertySource {
|
||||||
|
|
||||||
|
@AliasFor(annotation = PropertySource.class)
|
||||||
|
String value();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class MyCustomFactory implements PropertySourceFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public org.springframework.core.env.PropertySource createPropertySource(String name, EncodedResource resource) throws IOException {
|
||||||
|
Properties props = PropertiesLoaderUtils.loadProperties(resource);
|
||||||
|
return new org.springframework.core.env.PropertySource<Properties>("my" + name, props) {
|
||||||
|
@Override
|
||||||
|
public Object getProperty(String name) {
|
||||||
|
String value = props.getProperty(name);
|
||||||
|
return (value != null ? value.toUpperCase() : null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@PropertySource(
|
@PropertySource(
|
||||||
name = "psName",
|
name = "psName",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2016 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core.io.support;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.springframework.core.env.PropertySource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default implementation for {@link PropertySourceFactory},
|
||||||
|
* wrapping every resource in a {@link ResourcePropertySource}.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 4.3
|
||||||
|
* @see PropertySourceFactory
|
||||||
|
* @see ResourcePropertySource
|
||||||
|
*/
|
||||||
|
public class DefaultPropertySourceFactory implements PropertySourceFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
|
||||||
|
return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2016 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core.io.support;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.springframework.core.env.PropertySource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy interface for creating resource-based {@link PropertySource} wrappers.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 4.3
|
||||||
|
* @see DefaultPropertySourceFactory
|
||||||
|
*/
|
||||||
|
public interface PropertySourceFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link PropertySource} that wraps the given resource.
|
||||||
|
* @param name the name of the property source
|
||||||
|
* @param resource the resource (potentially encoded) to wrap
|
||||||
|
* @return the new {@link PropertySource} (never {@code null})
|
||||||
|
* @throws IOException if resource resolution failed
|
||||||
|
*/
|
||||||
|
PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException;
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue