PropertyDescriptorResolver Class — spring-boot Architecture
Architecture documentation for the PropertyDescriptorResolver class in PropertyDescriptorResolver.java from the spring-boot codebase.
Entity Profile
Source Code
configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/PropertyDescriptorResolver.java lines 44–241
class PropertyDescriptorResolver {
private final MetadataGenerationEnvironment environment;
PropertyDescriptorResolver(MetadataGenerationEnvironment environment) {
this.environment = environment;
}
/**
* Return the {@link PropertyDescriptor} instances that are valid candidates for the
* specified {@link TypeElement type} based on the specified {@link ExecutableElement
* factory method}, if any.
* @param type the target type
* @param factoryMethod the method that triggered the metadata for that {@code type}
* or {@code null}
* @return the candidate properties for metadata generation
*/
Stream<PropertyDescriptor> resolve(TypeElement type, ExecutableElement factoryMethod) {
TypeElementMembers members = new TypeElementMembers(this.environment, type);
if (factoryMethod != null) {
return resolveJavaBeanProperties(type, members, factoryMethod);
}
return resolve(Bindable.of(type, this.environment), members);
}
private Stream<PropertyDescriptor> resolve(Bindable bindable, TypeElementMembers members) {
if (bindable.isConstructorBindingEnabled()) {
ExecutableElement bindConstructor = bindable.getBindConstructor();
return (bindConstructor != null)
? resolveConstructorBoundProperties(bindable.getType(), members, bindConstructor) : Stream.empty();
}
return resolveJavaBeanProperties(bindable.getType(), members, null);
}
private Stream<PropertyDescriptor> resolveConstructorBoundProperties(TypeElement declaringElement,
TypeElementMembers members, ExecutableElement bindConstructor) {
Map<String, PropertyDescriptor> candidates = new LinkedHashMap<>();
bindConstructor.getParameters().forEach((parameter) -> {
PropertyDescriptor descriptor = extracted(declaringElement, members, parameter);
register(candidates, descriptor);
});
return candidates.values().stream();
}
private PropertyDescriptor extracted(TypeElement declaringElement, TypeElementMembers members,
VariableElement parameter) {
String parameterName = parameter.getSimpleName().toString();
String name = getPropertyName(parameter, parameterName);
TypeMirror type = parameter.asType();
ExecutableElement getter = members.getPublicGetter(parameterName, type);
ExecutableElement setter = members.getPublicSetter(parameterName, type);
VariableElement field = members.getFields().get(parameterName);
RecordComponentElement recordComponent = members.getRecordComponents().get(parameterName);
SourceMetadata sourceMetadata = this.environment.resolveSourceMetadata(field, getter);
PropertyDescriptor propertyDescriptor = (recordComponent != null)
? new RecordParameterPropertyDescriptor(name, type, parameter, declaringElement, getter,
recordComponent)
: new ConstructorParameterPropertyDescriptor(name, type, parameter, declaringElement, getter, setter,
field);
return sourceMetadata.createPropertyDescriptor(name, propertyDescriptor);
}
private String getPropertyName(VariableElement parameter, String fallback) {
AnnotationMirror nameAnnotation = this.environment.getNameAnnotation(parameter);
if (nameAnnotation != null) {
return this.environment.getAnnotationElementStringValue(nameAnnotation, "value");
}
return fallback;
}
private Stream<PropertyDescriptor> resolveJavaBeanProperties(TypeElement declaringElement,
TypeElementMembers members, ExecutableElement factoryMethod) {
// First check if we have regular java bean properties there
Map<String, PropertyDescriptor> candidates = new LinkedHashMap<>();
members.getPublicGetters().forEach((name, getters) -> {
VariableElement field = members.getFields().get(name);
ExecutableElement getter = findMatchingGetter(members, getters, field);
TypeMirror propertyType = getter.getReturnType();
SourceMetadata sourceMetadata = this.environment.resolveSourceMetadata(field, getter);
register(candidates,
sourceMetadata.createPropertyDescriptor(getPropertyName(field, name),
(propertyName) -> new JavaBeanPropertyDescriptor(propertyName, propertyType,
declaringElement, getter, members.getPublicSetter(name, propertyType), field,
factoryMethod)));
});
// Then check for Lombok ones
members.getFields().forEach((name, field) -> {
TypeMirror propertyType = field.asType();
ExecutableElement getter = members.getPublicGetter(name, propertyType);
ExecutableElement setter = members.getPublicSetter(name, propertyType);
SourceMetadata sourceMetadata = this.environment.resolveSourceMetadata(field, getter);
register(candidates,
sourceMetadata.createPropertyDescriptor(getPropertyName(field, name),
(propertyName) -> new LombokPropertyDescriptor(propertyName, propertyType, declaringElement,
getter, setter, field, factoryMethod)));
});
return candidates.values().stream();
}
private ExecutableElement findMatchingGetter(TypeElementMembers members, List<ExecutableElement> candidates,
VariableElement field) {
if (candidates.size() > 1 && field != null) {
return members.getMatchingGetter(candidates, field.asType());
}
return candidates.get(0);
}
private void register(Map<String, PropertyDescriptor> candidates, PropertyDescriptor descriptor) {
if (!candidates.containsKey(descriptor.getName()) && isCandidate(descriptor)) {
candidates.put(descriptor.getName(), descriptor);
}
}
private boolean isCandidate(PropertyDescriptor descriptor) {
return descriptor.isProperty(this.environment) || descriptor.isNested(this.environment);
}
/**
* Wrapper around a {@link TypeElement} that could be bound.
*/
private static class Bindable {
private final TypeElement type;
private final List<ExecutableElement> constructors;
private final List<ExecutableElement> boundConstructors;
Bindable(TypeElement type, List<ExecutableElement> constructors, List<ExecutableElement> boundConstructors) {
this.type = type;
this.constructors = constructors;
this.boundConstructors = boundConstructors;
}
TypeElement getType() {
return this.type;
}
boolean isConstructorBindingEnabled() {
return !this.boundConstructors.isEmpty();
}
ExecutableElement getBindConstructor() {
if (this.boundConstructors.isEmpty()) {
return findBoundConstructor();
}
if (this.boundConstructors.size() == 1) {
return this.boundConstructors.get(0);
}
return null;
}
private ExecutableElement findBoundConstructor() {
ExecutableElement boundConstructor = null;
for (ExecutableElement candidate : this.constructors) {
if (!candidate.getParameters().isEmpty()) {
if (boundConstructor != null) {
return null;
}
boundConstructor = candidate;
}
}
return boundConstructor;
}
static Bindable of(TypeElement type, MetadataGenerationEnvironment env) {
List<ExecutableElement> constructors = ElementFilter.constructorsIn(type.getEnclosedElements());
List<ExecutableElement> boundConstructors = getBoundConstructors(type, env, constructors);
return new Bindable(type, constructors, boundConstructors);
}
private static List<ExecutableElement> getBoundConstructors(TypeElement type, MetadataGenerationEnvironment env,
List<ExecutableElement> constructors) {
ExecutableElement bindConstructor = deduceBindConstructor(type, constructors, env);
if (bindConstructor != null) {
return Collections.singletonList(bindConstructor);
}
return constructors.stream().filter(env::hasConstructorBindingAnnotation).toList();
}
private static ExecutableElement deduceBindConstructor(TypeElement type, List<ExecutableElement> constructors,
MetadataGenerationEnvironment env) {
if (constructors.size() == 1) {
ExecutableElement candidate = constructors.get(0);
if (!candidate.getParameters().isEmpty() && !env.hasAutowiredAnnotation(candidate)) {
if (type.getNestingKind() == NestingKind.MEMBER
&& candidate.getModifiers().contains(Modifier.PRIVATE)) {
return null;
}
return candidate;
}
}
return null;
}
}
}
Source
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free