Support @Name annotation on /actuator/configprops

Update `ConfigurationPropertiesReportEndpoint` so that supports
constructor parameters annotated with `@Name`.

Fixes gh-24713
pull/24986/head
Phillip Webb 4 years ago
parent 6c2ff56fba
commit 26f143b8d3

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 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,6 +17,7 @@
package org.springframework.boot.actuate.context.properties; package org.springframework.boot.actuate.context.properties;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Parameter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -58,12 +59,16 @@ import org.springframework.boot.context.properties.BoundConfigurationProperties;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConfigurationPropertiesBean; import org.springframework.boot.context.properties.ConfigurationPropertiesBean;
import org.springframework.boot.context.properties.ConstructorBinding; import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.Name;
import org.springframework.boot.context.properties.source.ConfigurationProperty; import org.springframework.boot.context.properties.source.ConfigurationProperty;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.origin.Origin; import org.springframework.boot.origin.Origin;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.KotlinDetector; import org.springframework.core.KotlinDetector;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
@ -384,6 +389,8 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
*/ */
protected static class GenericSerializerModifier extends BeanSerializerModifier { protected static class GenericSerializerModifier extends BeanSerializerModifier {
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
@Override @Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) { List<BeanPropertyWriter> beanProperties) {
@ -398,11 +405,21 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
return result; return result;
} }
private boolean isCandidate(BeanDescription beanDesc, BeanPropertyWriter writer, private boolean isCandidate(BeanDescription beanDesc, BeanPropertyWriter writer, Constructor<?> constructor) {
Constructor<?> bindConstructor) { if (constructor != null) {
if (bindConstructor != null) { Parameter[] parameters = constructor.getParameters();
return Arrays.stream(bindConstructor.getParameters()) String[] names = PARAMETER_NAME_DISCOVERER.getParameterNames(constructor);
.anyMatch((parameter) -> parameter.getName().equals(writer.getName())); if (names == null) {
names = new String[parameters.length];
}
for (int i = 0; i < parameters.length; i++) {
String name = MergedAnnotations.from(parameters[i]).get(Name.class)
.getValue(MergedAnnotation.VALUE, String.class)
.orElse((names[i] != null) ? names[i] : parameters[i].getName());
if (name.equals(writer.getName())) {
return true;
}
}
} }
return isReadable(beanDesc, writer); return isReadable(beanDesc, writer);
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 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.
@ -34,6 +34,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding; import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue; import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.context.properties.bind.Name;
import org.springframework.boot.origin.Origin; import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginLookup; import org.springframework.boot.origin.OriginLookup;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
@ -73,7 +74,7 @@ class ConfigurationPropertiesReportEndpointTests {
void descriptorWithValueObjectBindMethodDetectsRelevantProperties() { void descriptorWithValueObjectBindMethodDetectsRelevantProperties() {
this.contextRunner.withUserConfiguration(ImmutablePropertiesConfiguration.class).run(assertProperties( this.contextRunner.withUserConfiguration(ImmutablePropertiesConfiguration.class).run(assertProperties(
"immutable", "immutable",
(properties) -> assertThat(properties).containsOnlyKeys("dbPassword", "myTestProperty", "duration"))); (properties) -> assertThat(properties).containsOnlyKeys("dbPassword", "myTestProperty", "for")));
} }
@Test @Test
@ -451,16 +452,16 @@ class ConfigurationPropertiesReportEndpointTests {
private final String nullValue; private final String nullValue;
private final Duration duration; private final Duration forDuration;
private final String ignored; private final String ignored;
ImmutableProperties(@DefaultValue("123456") String dbPassword, @DefaultValue("654321") String myTestProperty, ImmutableProperties(@DefaultValue("123456") String dbPassword, @DefaultValue("654321") String myTestProperty,
String nullValue, @DefaultValue("10s") Duration duration) { String nullValue, @DefaultValue("10s") @Name("for") Duration forDuration) {
this.dbPassword = dbPassword; this.dbPassword = dbPassword;
this.myTestProperty = myTestProperty; this.myTestProperty = myTestProperty;
this.nullValue = nullValue; this.nullValue = nullValue;
this.duration = duration; this.forDuration = forDuration;
this.ignored = "dummy"; this.ignored = "dummy";
} }
@ -476,8 +477,8 @@ class ConfigurationPropertiesReportEndpointTests {
return this.nullValue; return this.nullValue;
} }
public Duration getDuration() { public Duration getFor() {
return this.duration; return this.forDuration;
} }
public String getIgnored() { public String getIgnored() {

Loading…
Cancel
Save