Skip to content

Commit c53c8c8

Browse files
committed
Fix annotation processor to deal with relocated @ConstructorBinding
Update `ConfigurationMetadataAnnotationProcessor` to use the correct location for the `@ConstructorBinding` annotation and to deal with finding it as a meta-annotation. Closes gh-32660
1 parent bdedae2 commit c53c8c8

File tree

6 files changed

+134
-4
lines changed

6 files changed

+134
-4
lines changed

spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
7878

7979
static final String DEPRECATED_CONFIGURATION_PROPERTY_ANNOTATION = "org.springframework.boot.context.properties.DeprecatedConfigurationProperty";
8080

81-
static final String CONSTRUCTOR_BINDING_ANNOTATION = "org.springframework.boot.context.properties.ConstructorBinding";
81+
static final String CONSTRUCTOR_BINDING_ANNOTATION = "org.springframework.boot.context.properties.bind.ConstructorBinding";
8282

8383
static final String AUTOWIRED_ANNOTATION = "org.springframework.beans.factory.annotation.Autowired";
8484

spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironment.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import javax.lang.model.element.ExecutableElement;
3737
import javax.lang.model.element.TypeElement;
3838
import javax.lang.model.element.VariableElement;
39+
import javax.lang.model.type.DeclaredType;
3940
import javax.lang.model.type.TypeKind;
4041
import javax.lang.model.type.TypeMirror;
4142
import javax.lang.model.util.Elements;
@@ -184,15 +185,48 @@ ItemDeprecation resolveItemDeprecation(Element element) {
184185
}
185186

186187
boolean hasConstructorBindingAnnotation(ExecutableElement element) {
187-
return hasAnnotation(element, this.constructorBindingAnnotation);
188+
return hasAnnotation(element, this.constructorBindingAnnotation, true);
188189
}
189190

190191
boolean hasAutowiredAnnotation(ExecutableElement element) {
191192
return hasAnnotation(element, this.autowiredAnnotation);
192193
}
193194

194195
boolean hasAnnotation(Element element, String type) {
195-
return getAnnotation(element, type) != null;
196+
return hasAnnotation(element, type, false);
197+
}
198+
199+
boolean hasAnnotation(Element element, String type, boolean considerMetaAnnotations) {
200+
if (element != null) {
201+
for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
202+
if (type.equals(annotation.getAnnotationType().toString())) {
203+
return true;
204+
}
205+
}
206+
if (considerMetaAnnotations) {
207+
Set<Element> seen = new HashSet<>();
208+
for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
209+
if (hasMetaAnnotation(annotation.getAnnotationType().asElement(), type, seen)) {
210+
return true;
211+
}
212+
}
213+
214+
}
215+
}
216+
return false;
217+
}
218+
219+
private boolean hasMetaAnnotation(Element annotationElement, String type, Set<Element> seen) {
220+
if (seen.add(annotationElement)) {
221+
for (AnnotationMirror annotation : annotationElement.getAnnotationMirrors()) {
222+
DeclaredType annotationType = annotation.getAnnotationType();
223+
if (type.equals(annotationType.toString())
224+
|| hasMetaAnnotation(annotationType.asElement(), type, seen)) {
225+
return true;
226+
}
227+
}
228+
}
229+
return false;
196230
}
197231

198232
AnnotationMirror getAnnotation(Element element, String type) {

spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/PropertyDescriptorResolverTests.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.boot.configurationprocessor.metadata.ItemMetadata;
3333
import org.springframework.boot.configurationprocessor.test.RoundEnvironmentTester;
3434
import org.springframework.boot.configurationprocessor.test.TestableAnnotationProcessor;
35+
import org.springframework.boot.configurationsample.immutable.DeprecatedImmutableMultiConstructorProperties;
3536
import org.springframework.boot.configurationsample.immutable.ImmutableClassConstructorBindingProperties;
3637
import org.springframework.boot.configurationsample.immutable.ImmutableDeducedConstructorBindingProperties;
3738
import org.springframework.boot.configurationsample.immutable.ImmutableMultiConstructorProperties;
@@ -147,6 +148,16 @@ void propertiesWithMultiConstructor() throws IOException {
147148
.allMatch((predicate) -> predicate instanceof ConstructorParameterPropertyDescriptor)));
148149
}
149150

151+
@Test
152+
@Deprecated(since = "3.0.0", forRemoval = true)
153+
@SuppressWarnings("removal")
154+
void propertiesWithMultiConstructorAndDeprecatedAnnotation() throws IOException {
155+
process(DeprecatedImmutableMultiConstructorProperties.class,
156+
propertyNames((stream) -> assertThat(stream).containsExactly("name", "description")));
157+
process(DeprecatedImmutableMultiConstructorProperties.class, properties((stream) -> assertThat(stream)
158+
.allMatch((predicate) -> predicate instanceof ConstructorParameterPropertyDescriptor)));
159+
}
160+
150161
@Test
151162
void propertiesWithMultiConstructorNoDirective() throws IOException {
152163
process(TwoConstructorsExample.class, propertyNames((stream) -> assertThat(stream).containsExactly("name")));

spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/ConstructorBinding.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
*
2929
* @author Stephane Nicoll
3030
*/
31-
@Target(ElementType.CONSTRUCTOR)
31+
@Target({ ElementType.CONSTRUCTOR, ElementType.ANNOTATION_TYPE })
3232
@Retention(RetentionPolicy.RUNTIME)
3333
@Documented
3434
public @interface ConstructorBinding {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.configurationsample;
18+
19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
/**
26+
* Alternative to Spring Boot's deprecated
27+
* {@code @org.springframework.boot.context.properties.ConstructorBinding} for testing
28+
* (removes the need for a dependency on the real annotation).
29+
*
30+
* @author Stephane Nicoll
31+
*/
32+
@Target(ElementType.CONSTRUCTOR)
33+
@Retention(RetentionPolicy.RUNTIME)
34+
@Documented
35+
@ConstructorBinding
36+
@Deprecated(since = "3.0.0", forRemoval = true)
37+
public @interface DeprecatedConstructorBinding {
38+
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.configurationsample.immutable;
18+
19+
/**
20+
* Simple immutable properties with several constructors.
21+
*
22+
* @author Stephane Nicoll
23+
*/
24+
@SuppressWarnings("unused")
25+
@Deprecated(since = "3.0.0", forRemoval = true)
26+
public class DeprecatedImmutableMultiConstructorProperties {
27+
28+
private final String name;
29+
30+
/**
31+
* Test description.
32+
*/
33+
private final String description;
34+
35+
public DeprecatedImmutableMultiConstructorProperties(String name) {
36+
this(name, null);
37+
}
38+
39+
@SuppressWarnings("removal")
40+
@org.springframework.boot.configurationsample.DeprecatedConstructorBinding
41+
public DeprecatedImmutableMultiConstructorProperties(String name, String description) {
42+
this.name = name;
43+
this.description = description;
44+
}
45+
46+
}

0 commit comments

Comments
 (0)