diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java index 30b4d34eae..823f589aa6 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -81,6 +81,8 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor static final String READ_OPERATION_ANNOTATION = "org.springframework.boot.actuate." + "endpoint.annotation.ReadOperation"; + static final String NAME_ANNOTATION = "org.springframework.boot.context.properties.bind.Name"; + private static final Set SUPPORTED_OPTIONS = Collections .unmodifiableSet(Collections.singleton(ADDITIONAL_METADATA_LOCATIONS_OPTION)); @@ -118,6 +120,10 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor return READ_OPERATION_ANNOTATION; } + protected String nameAnnotation() { + return NAME_ANNOTATION; + } + @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); @@ -136,7 +142,7 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor this.metadataEnv = new MetadataGenerationEnvironment(env, configurationPropertiesAnnotation(), nestedConfigurationPropertyAnnotation(), deprecatedConfigurationPropertyAnnotation(), constructorBindingAnnotation(), defaultValueAnnotation(), endpointAnnotation(), - readOperationAnnotation()); + readOperationAnnotation(), nameAnnotation()); } @Override diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironment.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironment.java index 0fe689c165..b3006911ba 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironment.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironment.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -95,10 +95,12 @@ class MetadataGenerationEnvironment { private final String readOperationAnnotation; + private final String nameAnnotation; + MetadataGenerationEnvironment(ProcessingEnvironment environment, String configurationPropertiesAnnotation, String nestedConfigurationPropertyAnnotation, String deprecatedConfigurationPropertyAnnotation, String constructorBindingAnnotation, String defaultValueAnnotation, String endpointAnnotation, - String readOperationAnnotation) { + String readOperationAnnotation, String nameAnnotation) { this.typeUtils = new TypeUtils(environment); this.elements = environment.getElementUtils(); this.messager = environment.getMessager(); @@ -110,6 +112,7 @@ class MetadataGenerationEnvironment { this.defaultValueAnnotation = defaultValueAnnotation; this.endpointAnnotation = endpointAnnotation; this.readOperationAnnotation = readOperationAnnotation; + this.nameAnnotation = nameAnnotation; } private static FieldValuesParser resolveFieldValuesParser(ProcessingEnvironment env) { @@ -275,6 +278,10 @@ class MetadataGenerationEnvironment { return getAnnotation(element, this.readOperationAnnotation); } + AnnotationMirror getNameAnnotation(Element element) { + return getAnnotation(element, this.nameAnnotation); + } + boolean hasNullableAnnotation(Element element) { return getAnnotation(element, NULLABLE_ANNOTATION) != null; } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/PropertyDescriptorResolver.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/PropertyDescriptorResolver.java index fc8572d131..886b55f9d2 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/PropertyDescriptorResolver.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/PropertyDescriptorResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -22,6 +22,7 @@ import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.NestingKind; import javax.lang.model.element.TypeElement; @@ -76,7 +77,7 @@ class PropertyDescriptorResolver { TypeElementMembers members, ExecutableElement constructor) { Map> candidates = new LinkedHashMap<>(); constructor.getParameters().forEach((parameter) -> { - String name = parameter.getSimpleName().toString(); + String name = getParameterName(parameter); TypeMirror propertyType = parameter.asType(); ExecutableElement getter = members.getPublicGetter(name, propertyType); ExecutableElement setter = members.getPublicSetter(name, propertyType); @@ -87,6 +88,14 @@ class PropertyDescriptorResolver { return candidates.values().stream(); } + private String getParameterName(VariableElement parameter) { + AnnotationMirror nameAnnotation = this.environment.getNameAnnotation(parameter); + if (nameAnnotation != null) { + return (String) this.environment.getAnnotationElementValues(nameAnnotation).get("value"); + } + return parameter.getSimpleName().toString(); + } + Stream> resolveJavaBeanProperties(TypeElement type, ExecutableElement factoryMethod, TypeElementMembers members) { // First check if we have regular java bean properties there diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ImmutableNameAnnotationPropertiesTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ImmutableNameAnnotationPropertiesTests.java new file mode 100644 index 0000000000..6dac888b7e --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ImmutableNameAnnotationPropertiesTests.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012-2020 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.configurationprocessor; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata; +import org.springframework.boot.configurationprocessor.metadata.Metadata; +import org.springframework.boot.configurationsample.immutable.ImmutableNameAnnotationProperties; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Metadata generation tests for immutable properties using {@code @Name}. + * + * @author Phillip Webb + */ +public class ImmutableNameAnnotationPropertiesTests extends AbstractMetadataGenerationTests { + + @Test + void immutableNameAnnotationProperties() { + ConfigurationMetadata metadata = compile(ImmutableNameAnnotationProperties.class); + assertThat(metadata).has(Metadata.withProperty("named.import", String.class) + .fromSource(ImmutableNameAnnotationProperties.class)); + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironmentFactory.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironmentFactory.java index c8cf16531e..0b67c60a03 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironmentFactory.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironmentFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -38,7 +38,8 @@ class MetadataGenerationEnvironmentFactory implements Function assertThat(stream).element(0).isInstanceOf(JavaBeanPropertyDescriptor.class))); } + @Test + void propertiesWithNameAnnotationParameter() throws IOException { + process(ImmutableNameAnnotationProperties.class, + propertyNames((stream) -> assertThat(stream).containsExactly("import"))); + } + private BiConsumer properties( Consumer>> stream) { return (element, metadataEnv) -> { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/TestConfigurationMetadataAnnotationProcessor.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/TestConfigurationMetadataAnnotationProcessor.java index aeed80fdd7..149a746c0e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/TestConfigurationMetadataAnnotationProcessor.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/TestConfigurationMetadataAnnotationProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -55,6 +55,8 @@ public class TestConfigurationMetadataAnnotationProcessor extends ConfigurationM public static final String READ_OPERATION_ANNOTATION = "org.springframework.boot.configurationsample.ReadOperation"; + public static final String NAME_ANNOTATION = "org.springframework.boot.configurationsample.Name"; + private ConfigurationMetadata metadata; private final File outputLocation; @@ -98,6 +100,11 @@ public class TestConfigurationMetadataAnnotationProcessor extends ConfigurationM return READ_OPERATION_ANNOTATION; } + @Override + protected String nameAnnotation() { + return NAME_ANNOTATION; + } + @Override protected ConfigurationMetadata writeMetaData() throws Exception { super.writeMetaData(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/Name.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/Name.java new file mode 100644 index 0000000000..965f8f4c0f --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/Name.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-2020 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; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Alternative to Spring Boot's {@code @Name} for testing (removes the need for a + * dependency on the real annotation). + * + * @author Phillip Webb + */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Name { + + String value(); + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/immutable/ImmutableNameAnnotationProperties.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/immutable/ImmutableNameAnnotationProperties.java new file mode 100644 index 0000000000..e14baef82d --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/immutable/ImmutableNameAnnotationProperties.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012-2020 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.immutable; + +import org.springframework.boot.configurationsample.ConfigurationProperties; +import org.springframework.boot.configurationsample.ConstructorBinding; +import org.springframework.boot.configurationsample.Name; + +/** + * Immutable properties making use of {@code @Name}. + * + * @author Phillip Webb + */ +@ConfigurationProperties("named") +@ConstructorBinding +public class ImmutableNameAnnotationProperties { + + private String imports; + + public ImmutableNameAnnotationProperties(@Name("import") String imports) { + this.imports = imports; + } + + public String getImports() { + return this.imports; + } + +}