Merge branch '2.7.x'
Closes gh-33871
This commit is contained in:
@@ -59,23 +59,22 @@ class PropertyDescriptorResolver {
|
||||
if (factoryMethod != null) {
|
||||
return resolveJavaBeanProperties(type, factoryMethod, members);
|
||||
}
|
||||
return resolve(ConfigurationPropertiesTypeElement.of(type, this.environment), factoryMethod, members);
|
||||
return resolve(ConfigurationPropertiesTypeElement.of(type, this.environment), members);
|
||||
}
|
||||
|
||||
private Stream<PropertyDescriptor<?>> resolve(ConfigurationPropertiesTypeElement type,
|
||||
ExecutableElement factoryMethod, TypeElementMembers members) {
|
||||
private Stream<PropertyDescriptor<?>> resolve(ConfigurationPropertiesTypeElement type, TypeElementMembers members) {
|
||||
if (type.isConstructorBindingEnabled()) {
|
||||
ExecutableElement constructor = type.getBindConstructor();
|
||||
if (constructor != null) {
|
||||
return resolveConstructorProperties(type.getType(), factoryMethod, members, constructor);
|
||||
return resolveConstructorProperties(type.getType(), members, constructor);
|
||||
}
|
||||
return Stream.empty();
|
||||
}
|
||||
return resolveJavaBeanProperties(type.getType(), factoryMethod, members);
|
||||
return resolveJavaBeanProperties(type.getType(), null, members);
|
||||
}
|
||||
|
||||
Stream<PropertyDescriptor<?>> resolveConstructorProperties(TypeElement type, ExecutableElement factoryMethod,
|
||||
TypeElementMembers members, ExecutableElement constructor) {
|
||||
Stream<PropertyDescriptor<?>> resolveConstructorProperties(TypeElement type, TypeElementMembers members,
|
||||
ExecutableElement constructor) {
|
||||
Map<String, PropertyDescriptor<?>> candidates = new LinkedHashMap<>();
|
||||
constructor.getParameters().forEach((parameter) -> {
|
||||
String name = getParameterName(parameter);
|
||||
@@ -83,8 +82,8 @@ class PropertyDescriptorResolver {
|
||||
ExecutableElement getter = members.getPublicGetter(name, propertyType);
|
||||
ExecutableElement setter = members.getPublicSetter(name, propertyType);
|
||||
VariableElement field = members.getFields().get(name);
|
||||
register(candidates, new ConstructorParameterPropertyDescriptor(type, factoryMethod, parameter, name,
|
||||
propertyType, field, getter, setter));
|
||||
register(candidates, new ConstructorParameterPropertyDescriptor(type, null, parameter, name, propertyType,
|
||||
field, getter, setter));
|
||||
});
|
||||
return candidates.values().stream();
|
||||
}
|
||||
|
||||
@@ -38,15 +38,20 @@ import javax.lang.model.util.ElementFilter;
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class TypeElementMembers {
|
||||
|
||||
private static final String OBJECT_CLASS_NAME = Object.class.getName();
|
||||
|
||||
private static final String RECORD_CLASS_NAME = "java.lang.Record";
|
||||
|
||||
private final MetadataGenerationEnvironment env;
|
||||
|
||||
private final TypeElement targetType;
|
||||
|
||||
private final boolean isRecord;
|
||||
|
||||
private final Map<String, VariableElement> fields = new LinkedHashMap<>();
|
||||
|
||||
private final Map<String, List<ExecutableElement>> publicGetters = new LinkedHashMap<>();
|
||||
@@ -56,18 +61,20 @@ class TypeElementMembers {
|
||||
TypeElementMembers(MetadataGenerationEnvironment env, TypeElement targetType) {
|
||||
this.env = env;
|
||||
this.targetType = targetType;
|
||||
this.isRecord = RECORD_CLASS_NAME.equals(targetType.getSuperclass().toString());
|
||||
process(targetType);
|
||||
}
|
||||
|
||||
private void process(TypeElement element) {
|
||||
for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
|
||||
processMethod(method);
|
||||
}
|
||||
for (VariableElement field : ElementFilter.fieldsIn(element.getEnclosedElements())) {
|
||||
processField(field);
|
||||
}
|
||||
for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
|
||||
processMethod(method);
|
||||
}
|
||||
Element superType = this.env.getTypeUtils().asElement(element.getSuperclass());
|
||||
if (superType instanceof TypeElement && !OBJECT_CLASS_NAME.equals(superType.toString())) {
|
||||
if (superType instanceof TypeElement && !OBJECT_CLASS_NAME.equals(superType.toString())
|
||||
&& !RECORD_CLASS_NAME.equals(superType.toString())) {
|
||||
process((TypeElement) superType);
|
||||
}
|
||||
}
|
||||
@@ -122,12 +129,22 @@ class TypeElementMembers {
|
||||
}
|
||||
|
||||
private boolean isGetter(ExecutableElement method) {
|
||||
boolean hasParameters = !method.getParameters().isEmpty();
|
||||
boolean returnsVoid = TypeKind.VOID == method.getReturnType().getKind();
|
||||
if (hasParameters || returnsVoid) {
|
||||
return false;
|
||||
}
|
||||
String name = method.getSimpleName().toString();
|
||||
return ((name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2))
|
||||
&& method.getParameters().isEmpty() && (TypeKind.VOID != method.getReturnType().getKind());
|
||||
if (this.isRecord && this.fields.containsKey(name)) {
|
||||
return true;
|
||||
}
|
||||
return (name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2);
|
||||
}
|
||||
|
||||
private boolean isSetter(ExecutableElement method) {
|
||||
if (this.isRecord) {
|
||||
return false;
|
||||
}
|
||||
final String name = method.getSimpleName().toString();
|
||||
return (name.startsWith("set") && name.length() > 3 && method.getParameters().size() == 1
|
||||
&& isSetterReturnType(method));
|
||||
@@ -151,16 +168,29 @@ class TypeElementMembers {
|
||||
}
|
||||
|
||||
private String getAccessorName(String methodName) {
|
||||
String name = methodName.startsWith("is") ? methodName.substring(2) : methodName.substring(3);
|
||||
if (this.isRecord) {
|
||||
return methodName;
|
||||
}
|
||||
String name;
|
||||
if (methodName.startsWith("is")) {
|
||||
name = methodName.substring(2);
|
||||
}
|
||||
else if (methodName.startsWith("get")) {
|
||||
name = methodName.substring(3);
|
||||
}
|
||||
else if (methodName.startsWith("set")) {
|
||||
name = methodName.substring(3);
|
||||
}
|
||||
else {
|
||||
throw new AssertionError("methodName must start with 'is', 'get' or 'set', was '" + methodName + "'");
|
||||
}
|
||||
name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
|
||||
return name;
|
||||
}
|
||||
|
||||
private void processField(VariableElement field) {
|
||||
String name = field.getSimpleName().toString();
|
||||
if (!this.fields.containsKey(name)) {
|
||||
this.fields.put(name, field);
|
||||
}
|
||||
this.fields.putIfAbsent(name, field);
|
||||
}
|
||||
|
||||
Map<String, VariableElement> getFields() {
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.springframework.boot.configurationprocessor.metadata.Metadata;
|
||||
import org.springframework.boot.configurationsample.recursive.RecursiveProperties;
|
||||
import org.springframework.boot.configurationsample.simple.ClassWithNestedProperties;
|
||||
import org.springframework.boot.configurationsample.simple.DeprecatedFieldSingleProperty;
|
||||
import org.springframework.boot.configurationsample.simple.DeprecatedRecord;
|
||||
import org.springframework.boot.configurationsample.simple.DeprecatedSingleProperty;
|
||||
import org.springframework.boot.configurationsample.simple.DescriptionProperties;
|
||||
import org.springframework.boot.configurationsample.simple.HierarchicalProperties;
|
||||
@@ -219,6 +220,16 @@ class ConfigurationMetadataAnnotationProcessorTests extends AbstractMetadataGene
|
||||
.withNoDeprecation().fromSource(type));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deprecatedPropertyOnRecord() {
|
||||
Class<?> type = DeprecatedRecord.class;
|
||||
ConfigurationMetadata metadata = compile(type);
|
||||
assertThat(metadata).has(Metadata.withGroup("deprecated-record").fromSource(type));
|
||||
assertThat(metadata).has(Metadata.withProperty("deprecated-record.alpha", String.class).fromSource(type)
|
||||
.withDeprecation("some-reason", null));
|
||||
assertThat(metadata).has(Metadata.withProperty("deprecated-record.bravo", String.class).fromSource(type));
|
||||
}
|
||||
|
||||
@Test
|
||||
void typBoxing() {
|
||||
Class<?> type = BoxingPojo.class;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012-2023 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
|
||||
*
|
||||
* https://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.configurationsample.simple;
|
||||
|
||||
import org.springframework.boot.configurationsample.ConfigurationProperties;
|
||||
import org.springframework.boot.configurationsample.DeprecatedConfigurationProperty;
|
||||
|
||||
/**
|
||||
* Configuration properties as record with deprecated property.
|
||||
* @param alpha alpha property, deprecated
|
||||
* @param bravo bravo property
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
@ConfigurationProperties("deprecated-record")
|
||||
public record DeprecatedRecord(String alpha, String bravo) {
|
||||
|
||||
@Deprecated
|
||||
@DeprecatedConfigurationProperty(reason = "some-reason")
|
||||
public String alpha() {
|
||||
return this.alpha;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user