Add @Bean-level config properties to /configprops
This commit is contained in:
@@ -47,6 +47,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.bind.PropertiesConfigurationFactory;
|
||||
import org.springframework.boot.context.properties.ConfigurationBeanFactoryMetaData;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
@@ -71,13 +72,16 @@ public class EndpointAutoConfiguration {
|
||||
private InfoPropertiesConfiguration properties;
|
||||
|
||||
@Autowired(required = false)
|
||||
private final MetricReader metricRepository = new InMemoryMetricRepository();
|
||||
private MetricReader metricRepository = new InMemoryMetricRepository();
|
||||
|
||||
@Autowired(required = false)
|
||||
private PublicMetrics metrics;
|
||||
|
||||
@Autowired(required = false)
|
||||
private final TraceRepository traceRepository = new InMemoryTraceRepository();
|
||||
private TraceRepository traceRepository = new InMemoryTraceRepository();
|
||||
|
||||
@Autowired(required = false)
|
||||
private ConfigurationBeanFactoryMetaData beanFactoryMetaData;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@@ -159,7 +163,9 @@ public class EndpointAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ConfigurationPropertiesReportEndpoint configurationPropertiesReportEndpoint() {
|
||||
return new ConfigurationPropertiesReportEndpoint();
|
||||
ConfigurationPropertiesReportEndpoint endpoint = new ConfigurationPropertiesReportEndpoint();
|
||||
endpoint.setConfigurationBeanFactoryMetaData(this.beanFactoryMetaData);
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.boot.context.properties.ConfigurationBeanFactoryMetaData;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
@@ -47,6 +48,7 @@ import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
|
||||
* in your Spring Boot application configuration.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.configprops", ignoreUnknownFields = false)
|
||||
public class ConfigurationPropertiesReportEndpoint extends
|
||||
@@ -58,6 +60,8 @@ public class ConfigurationPropertiesReportEndpoint extends
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
private ConfigurationBeanFactoryMetaData beanFactoryMetaData;
|
||||
|
||||
public ConfigurationPropertiesReportEndpoint() {
|
||||
super("configprops");
|
||||
}
|
||||
@@ -71,6 +75,11 @@ public class ConfigurationPropertiesReportEndpoint extends
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void setConfigurationBeanFactoryMetaData(
|
||||
ConfigurationBeanFactoryMetaData beanFactoryMetaData) {
|
||||
this.beanFactoryMetaData = beanFactoryMetaData;
|
||||
}
|
||||
|
||||
public void setKeysToSanitize(String... keysToSanitize) {
|
||||
Assert.notNull(keysToSanitize, "KeysToSanitize must not be null");
|
||||
this.keysToSanitize = keysToSanitize;
|
||||
@@ -87,9 +96,14 @@ public class ConfigurationPropertiesReportEndpoint extends
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Map<String, Object> extract(ApplicationContext context) {
|
||||
|
||||
Map<String, Object> result = new HashMap<String, Object>();
|
||||
Map<String, Object> beans = context
|
||||
.getBeansWithAnnotation(ConfigurationProperties.class);
|
||||
Map<String, Object> beans = new HashMap<String, Object>(
|
||||
context.getBeansWithAnnotation(ConfigurationProperties.class));
|
||||
if (this.beanFactoryMetaData != null) {
|
||||
beans.putAll(this.beanFactoryMetaData
|
||||
.getBeansWithFactoryAnnotation(ConfigurationProperties.class));
|
||||
}
|
||||
|
||||
// Serialize beans into map structure and sanitize values
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
@@ -100,7 +114,7 @@ public class ConfigurationPropertiesReportEndpoint extends
|
||||
Object bean = entry.getValue();
|
||||
|
||||
Map<String, Object> root = new HashMap<String, Object>();
|
||||
root.put("prefix", extractPrefix(bean));
|
||||
root.put("prefix", extractPrefix(beanName, bean));
|
||||
root.put("properties", sanitize(mapper.convertValue(bean, Map.class)));
|
||||
result.put(beanName, root);
|
||||
}
|
||||
@@ -134,9 +148,19 @@ public class ConfigurationPropertiesReportEndpoint extends
|
||||
/**
|
||||
* Extract configuration prefix from {@link ConfigurationProperties} annotation.
|
||||
*/
|
||||
private String extractPrefix(Object bean) {
|
||||
private String extractPrefix(String beanName, Object bean) {
|
||||
ConfigurationProperties annotation = AnnotationUtils.findAnnotation(
|
||||
bean.getClass(), ConfigurationProperties.class);
|
||||
if (this.beanFactoryMetaData != null) {
|
||||
ConfigurationProperties override = this.beanFactoryMetaData
|
||||
.findFactoryAnnotation(beanName, ConfigurationProperties.class);
|
||||
if (override != null) {
|
||||
// The @Bean-level @ConfigurationProperties overrides the one at type
|
||||
// level when binding. Arguably we should render them both, but this one
|
||||
// might be the most relevant for a starting point.
|
||||
annotation = override;
|
||||
}
|
||||
}
|
||||
return (StringUtils.hasLength(annotation.value()) ? annotation.value()
|
||||
: annotation.prefix());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.boot.actuate.endpoint;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.context.properties.ConfigurationBeanFactoryMetaData;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class ConfigurationPropertiesReportEndpointMethodAnnotationsTests {
|
||||
|
||||
private AnnotationConfigApplicationContext context;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.context = new AnnotationConfigApplicationContext();
|
||||
}
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testNaming() throws Exception {
|
||||
this.context.register(Config.class);
|
||||
EnvironmentTestUtils.addEnvironment(this.context, "other.name:foo",
|
||||
"first.name:bar");
|
||||
this.context.refresh();
|
||||
ConfigurationPropertiesReportEndpoint report = this.context
|
||||
.getBean(ConfigurationPropertiesReportEndpoint.class);
|
||||
Map<String, Object> properties = report.invoke();
|
||||
Map<String, Object> nestedProperties = (Map<String, Object>) properties
|
||||
.get("other");
|
||||
assertNotNull(nestedProperties);
|
||||
assertEquals("other", nestedProperties.get("prefix"));
|
||||
assertNotNull(nestedProperties.get("properties"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testOverride() throws Exception {
|
||||
this.context.register(Other.class);
|
||||
EnvironmentTestUtils.addEnvironment(this.context, "other.name:foo");
|
||||
this.context.refresh();
|
||||
ConfigurationPropertiesReportEndpoint report = this.context
|
||||
.getBean(ConfigurationPropertiesReportEndpoint.class);
|
||||
Map<String, Object> properties = report.invoke();
|
||||
Map<String, Object> nestedProperties = (Map<String, Object>) properties
|
||||
.get("bar");
|
||||
assertNotNull(nestedProperties);
|
||||
assertEquals("other", nestedProperties.get("prefix"));
|
||||
assertNotNull(nestedProperties.get("properties"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
public static class Config {
|
||||
|
||||
@Bean
|
||||
public ConfigurationPropertiesReportEndpoint endpoint(
|
||||
ConfigurationBeanFactoryMetaData beanFactoryMetaData) {
|
||||
ConfigurationPropertiesReportEndpoint endpoint = new ConfigurationPropertiesReportEndpoint();
|
||||
endpoint.setConfigurationBeanFactoryMetaData(beanFactoryMetaData);
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "first")
|
||||
public Foo foo() {
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "other")
|
||||
public Foo other() {
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
public static class Other {
|
||||
|
||||
@Bean
|
||||
public ConfigurationPropertiesReportEndpoint endpoint(
|
||||
ConfigurationBeanFactoryMetaData beanFactoryMetaData) {
|
||||
ConfigurationPropertiesReportEndpoint endpoint = new ConfigurationPropertiesReportEndpoint();
|
||||
endpoint.setConfigurationBeanFactoryMetaData(beanFactoryMetaData);
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "other")
|
||||
public Bar bar() {
|
||||
return new Bar();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Foo {
|
||||
|
||||
private String name = "654321";
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "test")
|
||||
public static class Bar {
|
||||
|
||||
private String name = "654321";
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.boot.context.properties;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -25,6 +26,7 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodCallback;
|
||||
|
||||
@@ -34,7 +36,7 @@ import org.springframework.util.ReflectionUtils.MethodCallback;
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
class BeanMetaDataStore implements BeanFactoryPostProcessor {
|
||||
public class ConfigurationBeanFactoryMetaData implements BeanFactoryPostProcessor {
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
@@ -54,7 +56,26 @@ class BeanMetaDataStore implements BeanFactoryPostProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
public Method findFactoryMethod(String beanName) {
|
||||
public <A extends Annotation> Map<String, Object> getBeansWithFactoryAnnotation(
|
||||
Class<A> type) {
|
||||
Map<String, Object> result = new HashMap<String, Object>();
|
||||
for (String name : this.beans.keySet()) {
|
||||
if (findFactoryAnnotation(name, type) != null) {
|
||||
result.put(name, this.beanFactory.getBean(name));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public <A extends Annotation> A findFactoryAnnotation(String beanName, Class<A> type) {
|
||||
Method method = findFactoryMethod(beanName);
|
||||
if (method != null) {
|
||||
return AnnotationUtils.findAnnotation(method, type);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Method findFactoryMethod(String beanName) {
|
||||
if (!this.beans.containsKey(beanName)) {
|
||||
return null;
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.boot.context.properties;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
@@ -75,7 +74,7 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
||||
private static final String[] VALIDATOR_CLASSES = { "javax.validation.Validator",
|
||||
"javax.validation.ValidatorFactory" };
|
||||
|
||||
private BeanMetaDataStore beans = new BeanMetaDataStore();
|
||||
private ConfigurationBeanFactoryMetaData beans = new ConfigurationBeanFactoryMetaData();
|
||||
|
||||
private PropertySources propertySources;
|
||||
|
||||
@@ -138,7 +137,7 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
||||
/**
|
||||
* @param beans the bean meta data to set
|
||||
*/
|
||||
public void setBeanMetaDataStore(BeanMetaDataStore beans) {
|
||||
public void setBeanMetaDataStore(ConfigurationBeanFactoryMetaData beans) {
|
||||
this.beans = beans;
|
||||
}
|
||||
|
||||
@@ -287,24 +286,14 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
||||
if (annotation != null || bean instanceof ConfigurationPropertiesHolder) {
|
||||
postProcessBeforeInitialization(bean, beanName, annotation);
|
||||
}
|
||||
annotation = maybePostProcessAnnotatedFactoryMethod(bean, beanName);
|
||||
annotation = this.beans.findFactoryAnnotation(beanName,
|
||||
ConfigurationProperties.class);
|
||||
if (annotation != null) {
|
||||
postProcessBeforeInitialization(bean, beanName, annotation);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
private ConfigurationProperties maybePostProcessAnnotatedFactoryMethod(Object bean,
|
||||
String beanName) {
|
||||
Method method = this.beans.findFactoryMethod(beanName);
|
||||
if (method != null) {
|
||||
ConfigurationProperties annotation = AnnotationUtils.findAnnotation(method,
|
||||
ConfigurationProperties.class);
|
||||
return annotation;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
|
||||
@@ -41,7 +41,7 @@ public class ConfigurationPropertiesBindingPostProcessorRegistrar implements
|
||||
BeanDefinitionRegistry registry) {
|
||||
if (!registry.containsBeanDefinition(BINDER_BEAN_NAME)) {
|
||||
BeanDefinitionBuilder meta = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(BeanMetaDataStore.class);
|
||||
.genericBeanDefinition(ConfigurationBeanFactoryMetaData.class);
|
||||
BeanDefinitionBuilder bean = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(ConfigurationPropertiesBindingPostProcessor.class);
|
||||
bean.addPropertyReference("beanMetaDataStore", METADATA_BEAN_NAME);
|
||||
|
||||
Reference in New Issue
Block a user