diff --git a/spring-boot-docs/src/main/asciidoc/appendix-configuration-metadata.adoc b/spring-boot-docs/src/main/asciidoc/appendix-configuration-metadata.adoc
index 78ac6162831..bef5495654a 100644
--- a/spring-boot-docs/src/main/asciidoc/appendix-configuration-metadata.adoc
+++ b/spring-boot-docs/src/main/asciidoc/appendix-configuration-metadata.adoc
@@ -190,6 +190,10 @@ will be used to populate the `description` attribute.
NOTE: You should only use simple text with `@ConfigurationProperties` field Javadoc since
they are not processed before being added to the JSON.
+Properties are discovered via the presence of standard getters and setters with special
+handling for collection types (that will be detected even if only a getter is present). The
+annotation processor also supports the use of the `@Data`, `@Getter` and `@Setter` lombok
+annotations.
[[configuration-metadata-nested-properties]]
diff --git a/spring-boot-parent/pom.xml b/spring-boot-parent/pom.xml
index d60e2b4866f..1c6d221bcf8 100644
--- a/spring-boot-parent/pom.xml
+++ b/spring-boot-parent/pom.xml
@@ -191,6 +191,11 @@
json
${json.version}
+
+ org.projectlombok
+ lombok
+ 1.12.6
+
org.zeroturnaround
zt-zip
diff --git a/spring-boot-tools/spring-boot-configuration-processor/pom.xml b/spring-boot-tools/spring-boot-configuration-processor/pom.xml
index 49deddfa00e..3d2877d51d3 100644
--- a/spring-boot-tools/spring-boot-configuration-processor/pom.xml
+++ b/spring-boot-tools/spring-boot-configuration-processor/pom.xml
@@ -23,6 +23,12 @@
org.json
json
+
+
+ org.projectlombok
+ lombok
+ test
+
diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java b/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java
index 54347446e3b..85d153247ca 100644
--- a/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java
+++ b/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java
@@ -40,6 +40,7 @@ import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic.Kind;
import javax.tools.FileObject;
@@ -69,6 +70,12 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
static final String NESTED_CONFIGURATION_PROPERTY_ANNOTATION = "org.springframework.boot."
+ "context.properties.NestedConfigurationProperty";
+ static final String LOMBOK_DATA_ANNOTATION = "lombok.Data";
+
+ static final String LOMBOK_GETTER_ANNOTATION = "lombok.Getter";
+
+ static final String LOMBOK_SETTER_ANNOTATION = "lombok.Setter";
+
private ConfigurationMetadata metadata;
private TypeUtils typeUtils;
@@ -157,6 +164,7 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
TypeElementMembers members = new TypeElementMembers(this.processingEnv, element);
Map fieldValues = getFieldValues(element);
processSimpleTypes(prefix, element, members, fieldValues);
+ processLombokTypes(prefix, element, members, fieldValues);
processNestedTypes(prefix, element, members);
}
@@ -177,15 +185,14 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
ExecutableElement getter = entry.getValue();
ExecutableElement setter = members.getPublicSetters().get(name);
VariableElement field = members.getFields().get(name);
- Element returnType = this.processingEnv.getTypeUtils().asElement(
- getter.getReturnType());
- boolean isExcluded = this.typeExcludeFilter.isExcluded(getter
- .getReturnType());
- boolean isNested = isNested(returnType, field, element);
- boolean isCollection = this.typeUtils.isCollectionOrMap(getter
- .getReturnType());
+ TypeMirror returnType = getter.getReturnType();
+ Element returnTypeElement = this.processingEnv.getTypeUtils().asElement(
+ returnType);
+ boolean isExcluded = this.typeExcludeFilter.isExcluded(returnType);
+ boolean isNested = isNested(returnTypeElement, field, element);
+ boolean isCollection = this.typeUtils.isCollectionOrMap(returnType);
if (!isExcluded && !isNested && (setter != null || isCollection)) {
- String dataType = this.typeUtils.getType(getter.getReturnType());
+ String dataType = this.typeUtils.getType(returnType);
String sourceType = this.typeUtils.getType(element);
String description = this.typeUtils.getJavaDoc(field);
Object defaultValue = fieldValues.get(name);
@@ -198,6 +205,48 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
}
}
+ private void processLombokTypes(String prefix, TypeElement element,
+ TypeElementMembers members, Map fieldValues) {
+ for (Map.Entry entry : members.getFields()
+ .entrySet()) {
+ String name = entry.getKey();
+ VariableElement field = entry.getValue();
+ if (!isLombokField(field, element)) {
+ continue;
+ }
+ TypeMirror returnType = field.asType();
+ Element returnTypeElement = this.processingEnv.getTypeUtils().asElement(
+ returnType);
+ boolean isExcluded = this.typeExcludeFilter.isExcluded(returnType);
+ boolean isNested = isNested(returnTypeElement, field, element);
+ boolean isCollection = this.typeUtils.isCollectionOrMap(returnType);
+ boolean hasSetter = hasLombokSetter(field, element);
+ if (!isExcluded && !isNested && (hasSetter || isCollection)) {
+ String dataType = this.typeUtils.getType(returnType);
+ String sourceType = this.typeUtils.getType(element);
+ String description = this.typeUtils.getJavaDoc(field);
+ Object defaultValue = fieldValues.get(name);
+ boolean deprecated = hasDeprecateAnnotation(field)
+ || hasDeprecateAnnotation(element);
+ this.metadata.add(ItemMetadata.newProperty(prefix, name, dataType,
+ sourceType, null, description, defaultValue, deprecated));
+ }
+ }
+ }
+
+ private boolean isLombokField(VariableElement field, TypeElement element) {
+ return hasAnnotation(field, LOMBOK_GETTER_ANNOTATION)
+ || hasAnnotation(element, LOMBOK_GETTER_ANNOTATION)
+ || hasAnnotation(element, LOMBOK_DATA_ANNOTATION);
+ }
+
+ private boolean hasLombokSetter(VariableElement field, TypeElement element) {
+ return !field.getModifiers().contains(Modifier.FINAL) && (
+ hasAnnotation(field, LOMBOK_SETTER_ANNOTATION)
+ || hasAnnotation(element, LOMBOK_SETTER_ANNOTATION)
+ || hasAnnotation(element, LOMBOK_DATA_ANNOTATION));
+ }
+
private void processNestedTypes(String prefix, TypeElement element,
TypeElementMembers members) {
for (Map.Entry entry : members.getPublicGetters()
@@ -223,7 +272,7 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
private boolean isNested(Element returnType, VariableElement field,
TypeElement element) {
- if (getAnnotation(field, nestedConfigurationPropertyAnnotation()) != null) {
+ if (hasAnnotation(field, nestedConfigurationPropertyAnnotation())) {
return true;
}
return this.typeUtils.isEnclosedIn(returnType, element)
@@ -231,7 +280,11 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
}
private boolean hasDeprecateAnnotation(Element element) {
- return getAnnotation(element, "java.lang.Deprecated") != null;
+ return hasAnnotation(element, "java.lang.Deprecated");
+ }
+
+ private boolean hasAnnotation(Element element, String type) {
+ return getAnnotation(element, type) != null;
}
private AnnotationMirror getAnnotation(Element element, String type) {
diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java
index 58479cbe705..8a136381eb6 100644
--- a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java
+++ b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java
@@ -17,7 +17,6 @@
package org.springframework.boot.configurationprocessor;
import java.io.IOException;
-
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
@@ -25,7 +24,11 @@ import javax.lang.model.SourceVersion;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
+import org.springframework.boot.configurationsample.lombok.LombokSimpleDataProperties;
+import org.springframework.boot.configurationsample.lombok.LombokExplicitProperties;
+import org.springframework.boot.configurationsample.lombok.LombokSimpleProperties;
import org.springframework.boot.configurationsample.method.EmptyTypeMethodConfig;
import org.springframework.boot.configurationsample.method.InvalidMethodConfig;
import org.springframework.boot.configurationsample.method.MethodAndClassConfig;
@@ -286,13 +289,49 @@ public class ConfigurationMetadataAnnotationProcessorTests {
assertThat(metadata, not(containsProperty("excluded.writer-array")));
}
+ @Test
+ public void lombokDataProperties() throws Exception {
+ ConfigurationMetadata metadata = compile(LombokSimpleDataProperties.class);
+ assertSimpleLombokProperties(metadata, LombokSimpleDataProperties.class, "data");
+ }
+
+ @Test
+ public void lombokSimpleProperties() throws Exception {
+ ConfigurationMetadata metadata = compile(LombokSimpleProperties.class);
+ assertSimpleLombokProperties(metadata, LombokSimpleProperties.class, "simple");
+ }
+
+ @Test
+ public void lombokExplicitProperties() throws Exception {
+ ConfigurationMetadata metadata = compile(LombokExplicitProperties.class);
+ assertSimpleLombokProperties(metadata, LombokExplicitProperties.class, "explicit");
+ }
+
+ private void assertSimpleLombokProperties(ConfigurationMetadata metadata, Class> source, String prefix) {
+ assertThat(metadata, containsGroup(prefix).fromSource(source));
+ assertThat(metadata, not(containsProperty(prefix + ".id")));
+ assertThat(
+ metadata,
+ containsProperty(prefix + ".name", String.class)
+ .fromSource(source)
+ .withDescription("Name description."));
+ assertThat(metadata, containsProperty(prefix + ".description"));
+ assertThat(metadata, containsProperty(prefix + ".counter"));
+ assertThat(metadata,
+ containsProperty(prefix + ".number").fromSource(source)
+ .withDefaultValue(is(0))
+ .withDeprecated());
+ assertThat(metadata, containsProperty(prefix + ".items"));
+ assertThat(metadata, not(containsProperty(prefix + ".ignored")));
+ }
+
private ConfigurationMetadata compile(Class>... types) throws IOException {
TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor();
new TestCompiler(this.temporaryFolder).getTask(types).call(processor);
return processor.getMetadata();
}
- @SupportedAnnotationTypes({ "*" })
+ @SupportedAnnotationTypes({"*"})
@SupportedSourceVersion(SourceVersion.RELEASE_6)
private static class TestConfigurationMetadataAnnotationProcessor extends
ConfigurationMetadataAnnotationProcessor {
diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokExplicitProperties.java b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokExplicitProperties.java
new file mode 100644
index 00000000000..8ae45e6984a
--- /dev/null
+++ b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokExplicitProperties.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012-2014 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
+ *
+ * http://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.lombok;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import org.springframework.boot.configurationsample.ConfigurationProperties;
+
+/**
+ * Configuration properties using lombok @Getter/@Setter at field level.
+ *
+ * @author Stephane Nicoll
+ */
+@ConfigurationProperties(prefix = "explicit")
+public class LombokExplicitProperties {
+
+ @Getter
+ private final String id = "super-id";
+
+ /**
+ * Name description.
+ */
+ @Getter @Setter
+ private String name;
+
+ @Getter @Setter
+ private String description;
+
+ @Getter @Setter
+ private Integer counter;
+
+ @Deprecated @Getter @Setter
+ private Integer number = 0;
+
+ @Getter
+ private final List items = new ArrayList();
+
+ // Should be ignored if no annotation is set
+ private String ignored;
+
+}
diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokSimpleDataProperties.java b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokSimpleDataProperties.java
new file mode 100644
index 00000000000..31000b16c50
--- /dev/null
+++ b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokSimpleDataProperties.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012-2014 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
+ *
+ * http://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.lombok;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import lombok.Data;
+
+import org.springframework.boot.configurationsample.ConfigurationProperties;
+
+/**
+ * Configuration properties using lombok @Data.
+ *
+ * @author Stephane Nicoll
+ */
+@Data
+@ConfigurationProperties(prefix = "data")
+public class LombokSimpleDataProperties {
+
+ private final String id = "super-id";
+
+ /**
+ * Name description.
+ */
+ private String name;
+
+ private String description;
+
+ private Integer counter;
+
+ @Deprecated
+ private Integer number = 0;
+
+ private final List items = new ArrayList();
+
+ private final String ignored = "foo";
+
+}
diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokSimpleProperties.java b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokSimpleProperties.java
new file mode 100644
index 00000000000..899a0761d8c
--- /dev/null
+++ b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokSimpleProperties.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2012-2014 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
+ *
+ * http://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.lombok;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import org.springframework.boot.configurationsample.ConfigurationProperties;
+
+/**
+ * Configuration properties using lombok @Getter/@Setter at class level.
+ *
+ * @author Stephane Nicoll
+ */
+@Getter
+@Setter
+@ConfigurationProperties(prefix = "simple")
+public class LombokSimpleProperties {
+
+ private final String id = "super-id";
+
+ /**
+ * Name description.
+ */
+ private String name;
+
+ private String description;
+
+ private Integer counter;
+
+ @Deprecated
+ private Integer number = 0;
+
+ private final List items = new ArrayList();
+
+ private final String ignored = "foo";
+
+}