Support @Name annotation on /actuator/configprops

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

Fixes gh-24713
This commit is contained in:
Phillip Webb 2021-01-12 17:36:56 -08:00
parent 6c2ff56fba
commit 26f143b8d3
2 changed files with 31 additions and 13 deletions

View File

@ -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");
* you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package org.springframework.boot.actuate.context.properties;
import java.lang.reflect.Constructor;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
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.ConfigurationPropertiesBean;
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.ConfigurationPropertyName;
import org.springframework.boot.origin.Origin;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.DefaultParameterNameDiscoverer;
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.SearchStrategy;
import org.springframework.util.ClassUtils;
@ -384,6 +389,8 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
*/
protected static class GenericSerializerModifier extends BeanSerializerModifier {
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) {
@ -398,11 +405,21 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
return result;
}
private boolean isCandidate(BeanDescription beanDesc, BeanPropertyWriter writer,
Constructor<?> bindConstructor) {
if (bindConstructor != null) {
return Arrays.stream(bindConstructor.getParameters())
.anyMatch((parameter) -> parameter.getName().equals(writer.getName()));
private boolean isCandidate(BeanDescription beanDesc, BeanPropertyWriter writer, Constructor<?> constructor) {
if (constructor != null) {
Parameter[] parameters = constructor.getParameters();
String[] names = PARAMETER_NAME_DISCOVERER.getParameterNames(constructor);
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);
}

View File

@ -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");
* 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.EnableConfigurationProperties;
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.OriginLookup;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
@ -73,7 +74,7 @@ class ConfigurationPropertiesReportEndpointTests {
void descriptorWithValueObjectBindMethodDetectsRelevantProperties() {
this.contextRunner.withUserConfiguration(ImmutablePropertiesConfiguration.class).run(assertProperties(
"immutable",
(properties) -> assertThat(properties).containsOnlyKeys("dbPassword", "myTestProperty", "duration")));
(properties) -> assertThat(properties).containsOnlyKeys("dbPassword", "myTestProperty", "for")));
}
@Test
@ -451,16 +452,16 @@ class ConfigurationPropertiesReportEndpointTests {
private final String nullValue;
private final Duration duration;
private final Duration forDuration;
private final String ignored;
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.myTestProperty = myTestProperty;
this.nullValue = nullValue;
this.duration = duration;
this.forDuration = forDuration;
this.ignored = "dummy";
}
@ -476,8 +477,8 @@ class ConfigurationPropertiesReportEndpointTests {
return this.nullValue;
}
public Duration getDuration() {
return this.duration;
public Duration getFor() {
return this.forDuration;
}
public String getIgnored() {