Allow @ConstructorBinding to be optional
This commit makes @ConstructorBinding optional for a type that has a single parameterized constructor. An @Autowired annotation on any of the constructors indicates that the type should not be constructor bound. Since @ConstructorBinding is now deduced for a single parameterized constructor, the annotation is no longer needed at the type level. Closes gh-23216
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
@@ -79,6 +79,8 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
|
||||
|
||||
static final String CONSTRUCTOR_BINDING_ANNOTATION = "org.springframework.boot.context.properties.ConstructorBinding";
|
||||
|
||||
static final String AUTOWIRED_ANNOTATION = "org.springframework.beans.factory.annotation.Autowired";
|
||||
|
||||
static final String DEFAULT_VALUE_ANNOTATION = "org.springframework.boot.context.properties.bind.DefaultValue";
|
||||
|
||||
static final String CONTROLLER_ENDPOINT_ANNOTATION = "org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpoint";
|
||||
@@ -122,6 +124,10 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
|
||||
return CONSTRUCTOR_BINDING_ANNOTATION;
|
||||
}
|
||||
|
||||
protected String autowiredAnnotation() {
|
||||
return AUTOWIRED_ANNOTATION;
|
||||
}
|
||||
|
||||
protected String defaultValueAnnotation() {
|
||||
return DEFAULT_VALUE_ANNOTATION;
|
||||
}
|
||||
@@ -156,7 +162,7 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
|
||||
this.metadataCollector = new MetadataCollector(env, this.metadataStore.readMetadata());
|
||||
this.metadataEnv = new MetadataGenerationEnvironment(env, configurationPropertiesAnnotation(),
|
||||
nestedConfigurationPropertyAnnotation(), deprecatedConfigurationPropertyAnnotation(),
|
||||
constructorBindingAnnotation(), defaultValueAnnotation(), endpointAnnotations(),
|
||||
constructorBindingAnnotation(), autowiredAnnotation(), defaultValueAnnotation(), endpointAnnotations(),
|
||||
readOperationAnnotation(), nameAnnotation());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
@@ -99,10 +99,12 @@ class MetadataGenerationEnvironment {
|
||||
|
||||
private final String nameAnnotation;
|
||||
|
||||
private final String autowiredAnnotation;
|
||||
|
||||
MetadataGenerationEnvironment(ProcessingEnvironment environment, String configurationPropertiesAnnotation,
|
||||
String nestedConfigurationPropertyAnnotation, String deprecatedConfigurationPropertyAnnotation,
|
||||
String constructorBindingAnnotation, String defaultValueAnnotation, Set<String> endpointAnnotations,
|
||||
String readOperationAnnotation, String nameAnnotation) {
|
||||
String constructorBindingAnnotation, String autowiredAnnotation, String defaultValueAnnotation,
|
||||
Set<String> endpointAnnotations, String readOperationAnnotation, String nameAnnotation) {
|
||||
this.typeUtils = new TypeUtils(environment);
|
||||
this.elements = environment.getElementUtils();
|
||||
this.messager = environment.getMessager();
|
||||
@@ -111,6 +113,7 @@ class MetadataGenerationEnvironment {
|
||||
this.nestedConfigurationPropertyAnnotation = nestedConfigurationPropertyAnnotation;
|
||||
this.deprecatedConfigurationPropertyAnnotation = deprecatedConfigurationPropertyAnnotation;
|
||||
this.constructorBindingAnnotation = constructorBindingAnnotation;
|
||||
this.autowiredAnnotation = autowiredAnnotation;
|
||||
this.defaultValueAnnotation = defaultValueAnnotation;
|
||||
this.endpointAnnotations = endpointAnnotations;
|
||||
this.readOperationAnnotation = readOperationAnnotation;
|
||||
@@ -180,14 +183,14 @@ class MetadataGenerationEnvironment {
|
||||
return new ItemDeprecation(reason, replacement);
|
||||
}
|
||||
|
||||
boolean hasConstructorBindingAnnotation(TypeElement typeElement) {
|
||||
return hasAnnotationRecursive(typeElement, this.constructorBindingAnnotation);
|
||||
}
|
||||
|
||||
boolean hasConstructorBindingAnnotation(ExecutableElement element) {
|
||||
return hasAnnotation(element, this.constructorBindingAnnotation);
|
||||
}
|
||||
|
||||
boolean hasAutowiredAnnotation(ExecutableElement element) {
|
||||
return hasAnnotation(element, this.autowiredAnnotation);
|
||||
}
|
||||
|
||||
boolean hasAnnotation(Element element, String type) {
|
||||
return getAnnotation(element, type) != null;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.boot.configurationprocessor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -191,16 +192,29 @@ class PropertyDescriptorResolver {
|
||||
static ConfigurationPropertiesTypeElement of(TypeElement type, MetadataGenerationEnvironment env) {
|
||||
boolean constructorBoundType = isConstructorBoundType(type, env);
|
||||
List<ExecutableElement> constructors = ElementFilter.constructorsIn(type.getEnclosedElements());
|
||||
List<ExecutableElement> boundConstructors = constructors.stream()
|
||||
.filter(env::hasConstructorBindingAnnotation).collect(Collectors.toList());
|
||||
List<ExecutableElement> boundConstructors = getBoundConstructors(env, constructors);
|
||||
return new ConfigurationPropertiesTypeElement(type, constructorBoundType, constructors, boundConstructors);
|
||||
}
|
||||
|
||||
private static boolean isConstructorBoundType(TypeElement type, MetadataGenerationEnvironment env) {
|
||||
if (env.hasConstructorBindingAnnotation(type)
|
||||
|| "java.lang.Record".equals(type.getSuperclass().toString())) {
|
||||
return true;
|
||||
private static List<ExecutableElement> getBoundConstructors(MetadataGenerationEnvironment env,
|
||||
List<ExecutableElement> constructors) {
|
||||
ExecutableElement bindConstructor = deduceBindConstructor(constructors, env);
|
||||
if (bindConstructor != null) {
|
||||
return Collections.singletonList(bindConstructor);
|
||||
}
|
||||
return constructors.stream().filter(env::hasConstructorBindingAnnotation).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static ExecutableElement deduceBindConstructor(List<ExecutableElement> constructors,
|
||||
MetadataGenerationEnvironment env) {
|
||||
if (constructors.size() == 1 && constructors.get(0).getParameters().size() > 0
|
||||
&& !env.hasAutowiredAnnotation(constructors.get(0))) {
|
||||
return constructors.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isConstructorBoundType(TypeElement type, MetadataGenerationEnvironment env) {
|
||||
if (type.getNestingKind() == NestingKind.MEMBER) {
|
||||
return isConstructorBoundType((TypeElement) type.getEnclosingElement(), env);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user