mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
[bs-168] Support convenient binding of @Bean to external source
@ConfigurationProperties now has a path() attribute that can be used to specify a resource location explicitly. [Fixes #51968657]
This commit is contained in:
parent
15ba11f302
commit
d5aad97d1f
@ -38,8 +38,9 @@ import org.springframework.validation.ObjectError;
|
|||||||
import org.springframework.validation.Validator;
|
import org.springframework.validation.Validator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate some {@link Properties} by binding them to an object of a specified type and
|
* Validate some {@link Properties} (or optionally {@link PropertySources}) by binding
|
||||||
* then optionally running a {@link Validator} over it.
|
* them to an object of a specified type and then optionally running a {@link Validator}
|
||||||
|
* over it.
|
||||||
*
|
*
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*/
|
*/
|
||||||
@ -195,15 +196,13 @@ public class PropertiesConfigurationFactory<T> implements FactoryBean<T>,
|
|||||||
if (this.logger.isTraceEnabled()) {
|
if (this.logger.isTraceEnabled()) {
|
||||||
if (this.properties != null) {
|
if (this.properties != null) {
|
||||||
this.logger.trace("Properties:\n" + this.properties);
|
this.logger.trace("Properties:\n" + this.properties);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.logger.trace("Property Sources: " + this.propertySources);
|
this.logger.trace("Property Sources: " + this.propertySources);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.hasBeenBound = true;
|
this.hasBeenBound = true;
|
||||||
doBindPropertiesToTarget();
|
doBindPropertiesToTarget();
|
||||||
}
|
} catch (BindException ex) {
|
||||||
catch (BindException ex) {
|
|
||||||
if (this.exceptionIfInvalid) {
|
if (this.exceptionIfInvalid) {
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-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.bootstrap.config;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.springframework.bootstrap.config.YamlProcessor.DocumentMatcher;
|
||||||
|
import org.springframework.bootstrap.config.YamlProcessor.MatchStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link DocumentMatcher} that matches the default profile implicitly but not
|
||||||
|
* explicitly (i.e. matches if "spring.profiles" is not found and not otherwise).
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class DefaultProfileDocumentMatcher implements DocumentMatcher {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MatchStatus matches(Properties properties) {
|
||||||
|
if (!properties.containsKey("spring.profiles")) {
|
||||||
|
return MatchStatus.FOUND;
|
||||||
|
} else {
|
||||||
|
return MatchStatus.NOT_FOUND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-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.bootstrap.config;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.core.env.PropertiesPropertySource;
|
||||||
|
import org.springframework.core.env.PropertySource;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy to load '.properties' files into a {@link PropertySource}.
|
||||||
|
*/
|
||||||
|
public class PropertiesPropertySourceLoader implements PropertySourceLoader {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(Resource resource) {
|
||||||
|
return resource.getFilename().endsWith(".properties");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PropertySource<?> load(Resource resource, Environment environment) {
|
||||||
|
try {
|
||||||
|
Properties properties = loadProperties(resource, environment);
|
||||||
|
return new PropertiesPropertySource(resource.getDescription(), properties);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new IllegalStateException("Could not load properties from " + resource,
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Properties loadProperties(Resource resource, Environment environment)
|
||||||
|
throws IOException {
|
||||||
|
return PropertiesLoaderUtils.loadProperties(resource);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-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.bootstrap.config;
|
||||||
|
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.core.env.PropertySource;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy interface used to load a {@link PropertySource}.
|
||||||
|
*/
|
||||||
|
public interface PropertySourceLoader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Is this resource supported?
|
||||||
|
*/
|
||||||
|
public boolean supports(Resource resource);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the resource into a property source.
|
||||||
|
* @return a property source
|
||||||
|
*/
|
||||||
|
PropertySource<?> load(Resource resource, Environment environment);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-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.bootstrap.config;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.springframework.bootstrap.config.YamlProcessor.ArrayDocumentMatcher;
|
||||||
|
import org.springframework.bootstrap.config.YamlProcessor.DocumentMatcher;
|
||||||
|
import org.springframework.bootstrap.config.YamlProcessor.MatchStatus;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class SpringProfileDocumentMatcher implements DocumentMatcher {
|
||||||
|
|
||||||
|
private final Environment environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param environment
|
||||||
|
*/
|
||||||
|
public SpringProfileDocumentMatcher(Environment environment) {
|
||||||
|
this.environment = environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MatchStatus matches(Properties properties) {
|
||||||
|
String[] profiles = this.environment.getActiveProfiles();
|
||||||
|
if (profiles.length == 0) {
|
||||||
|
profiles = new String[] { "default" };
|
||||||
|
}
|
||||||
|
return new ArrayDocumentMatcher("spring.profiles", profiles).matches(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-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.bootstrap.config;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.springframework.bootstrap.config.YamlProcessor.DocumentMatcher;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.core.env.PropertySource;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy to load '.yml' files into a {@link PropertySource}.
|
||||||
|
*/
|
||||||
|
public class YamlPropertySourceLoader extends PropertiesPropertySourceLoader {
|
||||||
|
|
||||||
|
private List<DocumentMatcher> matchers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A property source loader that loads all properties and matches all documents.
|
||||||
|
*
|
||||||
|
* @return a property source loader
|
||||||
|
*/
|
||||||
|
public static YamlPropertySourceLoader matchAllLoader() {
|
||||||
|
return new YamlPropertySourceLoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A property source loader that matches documents that have no explicit profile or
|
||||||
|
* which have an explicit "spring.profiles.active" value in the current active
|
||||||
|
* profiles.
|
||||||
|
*
|
||||||
|
* @return a property source loader
|
||||||
|
*/
|
||||||
|
public static YamlPropertySourceLoader springProfileAwareLoader(
|
||||||
|
Environment environment) {
|
||||||
|
return new YamlPropertySourceLoader(new SpringProfileDocumentMatcher(environment),
|
||||||
|
new DefaultProfileDocumentMatcher());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param matchers
|
||||||
|
*/
|
||||||
|
public YamlPropertySourceLoader(DocumentMatcher... matchers) {
|
||||||
|
this.matchers = Arrays.asList(matchers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(Resource resource) {
|
||||||
|
return resource.getFilename().endsWith(".yml");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Properties loadProperties(final Resource resource,
|
||||||
|
final Environment environment) throws IOException {
|
||||||
|
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||||
|
if (this.matchers != null && !this.matchers.isEmpty()) {
|
||||||
|
factory.setMatchDefault(false);
|
||||||
|
factory.setDocumentMatchers(this.matchers);
|
||||||
|
}
|
||||||
|
factory.setResources(new Resource[] { resource });
|
||||||
|
return factory.getObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,26 +16,21 @@
|
|||||||
|
|
||||||
package org.springframework.bootstrap.context.initializer;
|
package org.springframework.bootstrap.context.initializer;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.springframework.bootstrap.config.YamlPropertiesFactoryBean;
|
import org.springframework.bootstrap.config.SpringProfileDocumentMatcher;
|
||||||
import org.springframework.bootstrap.config.YamlProcessor.ArrayDocumentMatcher;
|
import org.springframework.bootstrap.config.PropertiesPropertySourceLoader;
|
||||||
import org.springframework.bootstrap.config.YamlProcessor.DocumentMatcher;
|
import org.springframework.bootstrap.config.PropertySourceLoader;
|
||||||
import org.springframework.bootstrap.config.YamlProcessor.MatchStatus;
|
import org.springframework.bootstrap.config.YamlPropertySourceLoader;
|
||||||
import org.springframework.context.ApplicationContextInitializer;
|
import org.springframework.context.ApplicationContextInitializer;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.context.annotation.PropertySource;
|
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.env.CommandLinePropertySource;
|
import org.springframework.core.env.CommandLinePropertySource;
|
||||||
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
import org.springframework.core.env.MutablePropertySources;
|
import org.springframework.core.env.MutablePropertySources;
|
||||||
import org.springframework.core.env.PropertiesPropertySource;
|
import org.springframework.core.env.PropertySource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,8 +64,6 @@ import org.springframework.util.StringUtils;
|
|||||||
public class ConfigFileApplicationContextInitializer implements
|
public class ConfigFileApplicationContextInitializer implements
|
||||||
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
|
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
|
||||||
|
|
||||||
private static final Loader[] LOADERS = { new PropertiesLoader(), new YamlLoader() };
|
|
||||||
|
|
||||||
private static final String LOCATION_VARIABLE = "${spring.config.location}";
|
private static final String LOCATION_VARIABLE = "${spring.config.location}";
|
||||||
|
|
||||||
private String[] searchLocations = new String[] { "classpath:", "file:./",
|
private String[] searchLocations = new String[] { "classpath:", "file:./",
|
||||||
@ -100,29 +93,41 @@ public class ConfigFileApplicationContextInitializer implements
|
|||||||
private List<String> getCandidateLocations() {
|
private List<String> getCandidateLocations() {
|
||||||
List<String> candidates = new ArrayList<String>();
|
List<String> candidates = new ArrayList<String>();
|
||||||
for (String searchLocation : this.searchLocations) {
|
for (String searchLocation : this.searchLocations) {
|
||||||
for (Loader loader : LOADERS) {
|
for (String extension : new String[] { ".properties", ".yml" }) {
|
||||||
for (String extension : loader.getExtensions()) {
|
|
||||||
String location = searchLocation + this.name + extension;
|
String location = searchLocation + this.name + extension;
|
||||||
candidates.add(location);
|
candidates.add(location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
candidates.add(LOCATION_VARIABLE);
|
candidates.add(LOCATION_VARIABLE);
|
||||||
return candidates;
|
return candidates;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void load(ConfigurableApplicationContext applicationContext, String location,
|
private void load(ConfigurableApplicationContext applicationContext, String location,
|
||||||
String profile) {
|
String profile) {
|
||||||
location = applicationContext.getEnvironment().resolvePlaceholders(location);
|
|
||||||
|
ConfigurableEnvironment environment = applicationContext.getEnvironment();
|
||||||
|
location = environment.resolvePlaceholders(location);
|
||||||
String suffix = "." + StringUtils.getFilenameExtension(location);
|
String suffix = "." + StringUtils.getFilenameExtension(location);
|
||||||
if (StringUtils.hasLength(profile)) {
|
if (StringUtils.hasLength(profile)) {
|
||||||
location = location.replace(suffix, "-" + profile + suffix);
|
location = location.replace(suffix, "-" + profile + suffix);
|
||||||
}
|
}
|
||||||
for (Loader loader : LOADERS) {
|
PropertySourceLoader[] loaders = {
|
||||||
if (loader.getExtensions().contains(suffix.toLowerCase())) {
|
new PropertiesPropertySourceLoader(),
|
||||||
|
new YamlPropertySourceLoader(new SpringProfileDocumentMatcher(environment),
|
||||||
|
new ProfileSettingDocumentMatcher(environment)) };
|
||||||
|
for (PropertySourceLoader loader : loaders) {
|
||||||
Resource resource = applicationContext.getResource(location);
|
Resource resource = applicationContext.getResource(location);
|
||||||
if (resource != null && resource.exists()) {
|
if (resource != null && resource.exists() && loader.supports(resource)) {
|
||||||
loader.load(resource, applicationContext);
|
PropertySource<?> propertySource = loader.load(resource, environment);
|
||||||
|
MutablePropertySources propertySources = environment.getPropertySources();
|
||||||
|
if (propertySources
|
||||||
|
.contains(CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) {
|
||||||
|
propertySources.addAfter(
|
||||||
|
CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
|
||||||
|
propertySource);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
propertySources.addFirst(propertySource);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -152,118 +157,4 @@ public class ConfigFileApplicationContextInitializer implements
|
|||||||
this.searchLocations = (searchLocations == null ? null : searchLocations.clone());
|
this.searchLocations = (searchLocations == null ? null : searchLocations.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Strategy interface used to load a {@link PropertySource}.
|
|
||||||
*/
|
|
||||||
private static interface Loader {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The supported extensions (including '.' and in lowercase)
|
|
||||||
*/
|
|
||||||
public Set<String> getExtensions();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the resource into the destination application context.
|
|
||||||
*/
|
|
||||||
void load(Resource resource, ConfigurableApplicationContext applicationContext);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Strategy to load '.properties' files.
|
|
||||||
*/
|
|
||||||
private static class PropertiesLoader implements Loader {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<String> getExtensions() {
|
|
||||||
return Collections.singleton(".properties");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void load(Resource resource,
|
|
||||||
ConfigurableApplicationContext applicationContext) {
|
|
||||||
try {
|
|
||||||
Properties properties = loadProperties(resource, applicationContext);
|
|
||||||
MutablePropertySources propertySources = applicationContext
|
|
||||||
.getEnvironment().getPropertySources();
|
|
||||||
if (propertySources
|
|
||||||
.contains(CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) {
|
|
||||||
propertySources.addAfter(
|
|
||||||
CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
|
|
||||||
new PropertiesPropertySource(resource.getDescription(),
|
|
||||||
properties));
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
propertySources.addFirst(new PropertiesPropertySource(resource
|
|
||||||
.getDescription(), properties));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new IllegalStateException("Could not load properties file from "
|
|
||||||
+ resource, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Properties loadProperties(Resource resource,
|
|
||||||
ConfigurableApplicationContext applicationContext) throws IOException {
|
|
||||||
return PropertiesLoaderUtils.loadProperties(resource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Strategy to load '.yml' files.
|
|
||||||
*/
|
|
||||||
private static class YamlLoader extends PropertiesLoader {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<String> getExtensions() {
|
|
||||||
return Collections.singleton(".yml");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Properties loadProperties(final Resource resource,
|
|
||||||
final ConfigurableApplicationContext applicationContext)
|
|
||||||
throws IOException {
|
|
||||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
|
||||||
List<DocumentMatcher> matchers = new ArrayList<DocumentMatcher>();
|
|
||||||
matchers.add(new DocumentMatcher() {
|
|
||||||
@Override
|
|
||||||
public MatchStatus matches(Properties properties) {
|
|
||||||
String[] profiles = applicationContext.getEnvironment()
|
|
||||||
.getActiveProfiles();
|
|
||||||
if (profiles.length == 0) {
|
|
||||||
profiles = new String[] { "default" };
|
|
||||||
}
|
|
||||||
return new ArrayDocumentMatcher("spring.profiles", profiles)
|
|
||||||
.matches(properties);
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
matchers.add(new DocumentMatcher() {
|
|
||||||
@Override
|
|
||||||
public MatchStatus matches(Properties properties) {
|
|
||||||
if (!properties.containsKey("spring.profiles")) {
|
|
||||||
Set<String> profiles = StringUtils
|
|
||||||
.commaDelimitedListToSet(properties.getProperty(
|
|
||||||
"spring.profiles.active", ""));
|
|
||||||
for (String profile : profiles) {
|
|
||||||
// allow document with no profile to set the active one
|
|
||||||
applicationContext.getEnvironment().addActiveProfile(profile);
|
|
||||||
}
|
|
||||||
// matches default profile
|
|
||||||
return MatchStatus.FOUND;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return MatchStatus.NOT_FOUND;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
factory.setMatchDefault(false);
|
|
||||||
factory.setDocumentMatchers(matchers);
|
|
||||||
factory.setResources(new Resource[] { resource });
|
|
||||||
return factory.getObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-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.bootstrap.context.initializer;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.springframework.bootstrap.config.YamlProcessor.DocumentMatcher;
|
||||||
|
import org.springframework.bootstrap.config.YamlProcessor.MatchStatus;
|
||||||
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link DocumentMatcher} that sets the active profile if it finds a document with
|
||||||
|
* a key <code>spring.profiles.active</code>.
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class ProfileSettingDocumentMatcher implements DocumentMatcher {
|
||||||
|
|
||||||
|
private final Environment environment;
|
||||||
|
|
||||||
|
public ProfileSettingDocumentMatcher(Environment environment) {
|
||||||
|
this.environment = environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MatchStatus matches(Properties properties) {
|
||||||
|
if (!properties.containsKey("spring.profiles")) {
|
||||||
|
Set<String> profiles = StringUtils.commaDelimitedListToSet(properties
|
||||||
|
.getProperty("spring.profiles.active", ""));
|
||||||
|
if (this.environment instanceof ConfigurableEnvironment) {
|
||||||
|
ConfigurableEnvironment configurable = (ConfigurableEnvironment) this.environment;
|
||||||
|
for (String profile : profiles) {
|
||||||
|
// allow document with no profile to set the active one
|
||||||
|
configurable.addActiveProfile(profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// matches default profile
|
||||||
|
return MatchStatus.FOUND;
|
||||||
|
} else {
|
||||||
|
return MatchStatus.NOT_FOUND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -70,4 +70,12 @@ public @interface ConfigurationProperties {
|
|||||||
*/
|
*/
|
||||||
boolean ignoreUnknownFields() default true;
|
boolean ignoreUnknownFields() default true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionally provide an explicit resource path to bind to instead of using the
|
||||||
|
* default environment.
|
||||||
|
*
|
||||||
|
* @return the path (or paths) of resources to bind to
|
||||||
|
*/
|
||||||
|
String[] path() default {};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,23 @@ import org.springframework.beans.factory.BeanFactoryAware;
|
|||||||
import org.springframework.beans.factory.ListableBeanFactory;
|
import org.springframework.beans.factory.ListableBeanFactory;
|
||||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||||
import org.springframework.bootstrap.bind.PropertiesConfigurationFactory;
|
import org.springframework.bootstrap.bind.PropertiesConfigurationFactory;
|
||||||
|
import org.springframework.bootstrap.config.PropertiesPropertySourceLoader;
|
||||||
|
import org.springframework.bootstrap.config.PropertySourceLoader;
|
||||||
|
import org.springframework.bootstrap.config.YamlPropertySourceLoader;
|
||||||
|
import org.springframework.context.EnvironmentAware;
|
||||||
|
import org.springframework.context.ResourceLoaderAware;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.core.convert.ConversionService;
|
import org.springframework.core.convert.ConversionService;
|
||||||
import org.springframework.core.convert.converter.Converter;
|
import org.springframework.core.convert.converter.Converter;
|
||||||
import org.springframework.core.convert.support.DefaultConversionService;
|
import org.springframework.core.convert.support.DefaultConversionService;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.core.env.MutablePropertySources;
|
||||||
|
import org.springframework.core.env.PropertySource;
|
||||||
import org.springframework.core.env.PropertySources;
|
import org.springframework.core.env.PropertySources;
|
||||||
|
import org.springframework.core.env.StandardEnvironment;
|
||||||
|
import org.springframework.core.io.DefaultResourceLoader;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.validation.Validator;
|
import org.springframework.validation.Validator;
|
||||||
|
|
||||||
@ -38,7 +50,7 @@ import org.springframework.validation.Validator;
|
|||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*/
|
*/
|
||||||
public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor,
|
public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor,
|
||||||
BeanFactoryAware {
|
BeanFactoryAware, ResourceLoaderAware, EnvironmentAware {
|
||||||
|
|
||||||
private PropertySources propertySources;
|
private PropertySources propertySources;
|
||||||
|
|
||||||
@ -52,6 +64,10 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
|||||||
|
|
||||||
private boolean initialized = false;
|
private boolean initialized = false;
|
||||||
|
|
||||||
|
private ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||||
|
|
||||||
|
private Environment environment = new StandardEnvironment();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param propertySources
|
* @param propertySources
|
||||||
*/
|
*/
|
||||||
@ -78,6 +94,16 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
|||||||
this.beanFactory = beanFactory;
|
this.beanFactory = beanFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||||
|
this.resourceLoader = resourceLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEnvironment(Environment environment) {
|
||||||
|
this.environment = environment;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||||
throws BeansException {
|
throws BeansException {
|
||||||
@ -101,7 +127,12 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
|||||||
.getTarget() : bean);
|
.getTarget() : bean);
|
||||||
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
|
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
|
||||||
target);
|
target);
|
||||||
|
if (annotation != null && annotation.path().length != 0) {
|
||||||
|
|
||||||
|
factory.setPropertySources(loadPropertySources(annotation.path()));
|
||||||
|
} else {
|
||||||
factory.setPropertySources(this.propertySources);
|
factory.setPropertySources(this.propertySources);
|
||||||
|
}
|
||||||
factory.setValidator(this.validator);
|
factory.setValidator(this.validator);
|
||||||
// If no explicit conversion service is provided we add one so that (at least)
|
// If no explicit conversion service is provided we add one so that (at least)
|
||||||
// comma-separated arrays of convertibles can be bound automatically
|
// comma-separated arrays of convertibles can be bound automatically
|
||||||
@ -118,12 +149,31 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
factory.bindPropertiesToTarget();
|
factory.bindPropertiesToTarget();
|
||||||
}
|
} catch (Exception ex) {
|
||||||
catch (Exception ex) {
|
|
||||||
throw new BeanCreationException(beanName, "Could not bind properties", ex);
|
throw new BeanCreationException(beanName, "Could not bind properties", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PropertySources loadPropertySources(String[] path) {
|
||||||
|
MutablePropertySources propertySources = new MutablePropertySources();
|
||||||
|
PropertySourceLoader[] loaders = { new PropertiesPropertySourceLoader(),
|
||||||
|
YamlPropertySourceLoader.springProfileAwareLoader(this.environment) };
|
||||||
|
for (String location : path) {
|
||||||
|
location = this.environment.resolvePlaceholders(location);
|
||||||
|
Resource resource = this.resourceLoader.getResource(location);
|
||||||
|
if (resource != null && resource.exists()) {
|
||||||
|
for (PropertySourceLoader loader : loaders) {
|
||||||
|
if (loader.supports(resource)) {
|
||||||
|
PropertySource<?> propertySource = loader.load(resource,
|
||||||
|
this.environment);
|
||||||
|
propertySources.addFirst(propertySource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return propertySources;
|
||||||
|
}
|
||||||
|
|
||||||
private ConversionService getDefaultConversionService() {
|
private ConversionService getDefaultConversionService() {
|
||||||
if (!this.initialized && this.beanFactory instanceof ListableBeanFactory) {
|
if (!this.initialized && this.beanFactory instanceof ListableBeanFactory) {
|
||||||
for (Converter<?, ?> converter : ((ListableBeanFactory) this.beanFactory)
|
for (Converter<?, ?> converter : ((ListableBeanFactory) this.beanFactory)
|
||||||
|
@ -120,6 +120,49 @@ public class EnableConfigurationPropertiesTests {
|
|||||||
assertEquals("bar", this.context.getBean(TestProperties.class).getName());
|
assertEquals("bar", this.context.getBean(TestProperties.class).getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBindingDirectlyToFile() {
|
||||||
|
this.context.register(ResourceBindingProperties.class, TestConfiguration.class);
|
||||||
|
this.context.refresh();
|
||||||
|
assertEquals(1,
|
||||||
|
this.context.getBeanNamesForType(ResourceBindingProperties.class).length);
|
||||||
|
assertEquals("foo", this.context.getBean(ResourceBindingProperties.class)
|
||||||
|
.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBindingDirectlyToFileResolvedFromEnvironment() {
|
||||||
|
TestUtils.addEnviroment(this.context, "binding.location:classpath:other.yml");
|
||||||
|
this.context.register(ResourceBindingProperties.class, TestConfiguration.class);
|
||||||
|
this.context.refresh();
|
||||||
|
assertEquals(1,
|
||||||
|
this.context.getBeanNamesForType(ResourceBindingProperties.class).length);
|
||||||
|
assertEquals("other", this.context.getBean(ResourceBindingProperties.class)
|
||||||
|
.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBindingDirectlyToFileWithDefaultsWhenProfileNotFound() {
|
||||||
|
this.context.register(ResourceBindingProperties.class, TestConfiguration.class);
|
||||||
|
this.context.getEnvironment().addActiveProfile("nonexistent");
|
||||||
|
this.context.refresh();
|
||||||
|
assertEquals(1,
|
||||||
|
this.context.getBeanNamesForType(ResourceBindingProperties.class).length);
|
||||||
|
assertEquals("foo", this.context.getBean(ResourceBindingProperties.class)
|
||||||
|
.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBindingDirectlyToFileWithExplicitSpringProfile() {
|
||||||
|
this.context.register(ResourceBindingProperties.class, TestConfiguration.class);
|
||||||
|
this.context.getEnvironment().addActiveProfile("super");
|
||||||
|
this.context.refresh();
|
||||||
|
assertEquals(1,
|
||||||
|
this.context.getBeanNamesForType(ResourceBindingProperties.class).length);
|
||||||
|
assertEquals("bar", this.context.getBean(ResourceBindingProperties.class)
|
||||||
|
.getName());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBindingWithTwoBeans() {
|
public void testBindingWithTwoBeans() {
|
||||||
this.context.register(MoreConfiguration.class, TestConfiguration.class);
|
this.context.register(MoreConfiguration.class, TestConfiguration.class);
|
||||||
@ -246,4 +289,16 @@ public class EnableConfigurationPropertiesTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ConfigurationProperties(path = "${binding.location:classpath:name.yml}")
|
||||||
|
protected static class ResourceBindingProperties {
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
10
spring-bootstrap/src/test/resources/name.yml
Normal file
10
spring-bootstrap/src/test/resources/name.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
name: foo
|
||||||
|
|
||||||
|
---
|
||||||
|
spring.profiles: super
|
||||||
|
name: bar
|
||||||
|
|
||||||
|
---
|
||||||
|
spring.profiles: other
|
||||||
|
name: spam
|
2
spring-bootstrap/src/test/resources/other.yml
Normal file
2
spring-bootstrap/src/test/resources/other.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
name: other
|
Loading…
Reference in New Issue
Block a user