Support of Lombok annotated ConfigurationProperties
Previously, no configuration properties were discovered on a class using lombok instead of regular getters/setters. This commit adds a support for some of the lombok annotations, specifically that is @Data, @Getter and @Setter. Provides the same semantic as what lombok is generating. Closes gh-2114
This commit is contained in:
parent
82c6142166
commit
77427f53cc
|
|
@ -190,6 +190,10 @@ will be used to populate the `description` attribute.
|
||||||
NOTE: You should only use simple text with `@ConfigurationProperties` field Javadoc since
|
NOTE: You should only use simple text with `@ConfigurationProperties` field Javadoc since
|
||||||
they are not processed before being added to the JSON.
|
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]]
|
[[configuration-metadata-nested-properties]]
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,11 @@
|
||||||
<artifactId>json</artifactId>
|
<artifactId>json</artifactId>
|
||||||
<version>${json.version}</version>
|
<version>${json.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.12.6</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.zeroturnaround</groupId>
|
<groupId>org.zeroturnaround</groupId>
|
||||||
<artifactId>zt-zip</artifactId>
|
<artifactId>zt-zip</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,12 @@
|
||||||
<groupId>org.json</groupId>
|
<groupId>org.json</groupId>
|
||||||
<artifactId>json</artifactId>
|
<artifactId>json</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ import javax.lang.model.element.Modifier;
|
||||||
import javax.lang.model.element.TypeElement;
|
import javax.lang.model.element.TypeElement;
|
||||||
import javax.lang.model.element.VariableElement;
|
import javax.lang.model.element.VariableElement;
|
||||||
import javax.lang.model.type.TypeKind;
|
import javax.lang.model.type.TypeKind;
|
||||||
|
import javax.lang.model.type.TypeMirror;
|
||||||
import javax.lang.model.util.Elements;
|
import javax.lang.model.util.Elements;
|
||||||
import javax.tools.Diagnostic.Kind;
|
import javax.tools.Diagnostic.Kind;
|
||||||
import javax.tools.FileObject;
|
import javax.tools.FileObject;
|
||||||
|
|
@ -69,6 +70,12 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
|
||||||
static final String NESTED_CONFIGURATION_PROPERTY_ANNOTATION = "org.springframework.boot."
|
static final String NESTED_CONFIGURATION_PROPERTY_ANNOTATION = "org.springframework.boot."
|
||||||
+ "context.properties.NestedConfigurationProperty";
|
+ "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 ConfigurationMetadata metadata;
|
||||||
|
|
||||||
private TypeUtils typeUtils;
|
private TypeUtils typeUtils;
|
||||||
|
|
@ -157,6 +164,7 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
|
||||||
TypeElementMembers members = new TypeElementMembers(this.processingEnv, element);
|
TypeElementMembers members = new TypeElementMembers(this.processingEnv, element);
|
||||||
Map<String, Object> fieldValues = getFieldValues(element);
|
Map<String, Object> fieldValues = getFieldValues(element);
|
||||||
processSimpleTypes(prefix, element, members, fieldValues);
|
processSimpleTypes(prefix, element, members, fieldValues);
|
||||||
|
processLombokTypes(prefix, element, members, fieldValues);
|
||||||
processNestedTypes(prefix, element, members);
|
processNestedTypes(prefix, element, members);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,15 +185,14 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
|
||||||
ExecutableElement getter = entry.getValue();
|
ExecutableElement getter = entry.getValue();
|
||||||
ExecutableElement setter = members.getPublicSetters().get(name);
|
ExecutableElement setter = members.getPublicSetters().get(name);
|
||||||
VariableElement field = members.getFields().get(name);
|
VariableElement field = members.getFields().get(name);
|
||||||
Element returnType = this.processingEnv.getTypeUtils().asElement(
|
TypeMirror returnType = getter.getReturnType();
|
||||||
getter.getReturnType());
|
Element returnTypeElement = this.processingEnv.getTypeUtils().asElement(
|
||||||
boolean isExcluded = this.typeExcludeFilter.isExcluded(getter
|
returnType);
|
||||||
.getReturnType());
|
boolean isExcluded = this.typeExcludeFilter.isExcluded(returnType);
|
||||||
boolean isNested = isNested(returnType, field, element);
|
boolean isNested = isNested(returnTypeElement, field, element);
|
||||||
boolean isCollection = this.typeUtils.isCollectionOrMap(getter
|
boolean isCollection = this.typeUtils.isCollectionOrMap(returnType);
|
||||||
.getReturnType());
|
|
||||||
if (!isExcluded && !isNested && (setter != null || isCollection)) {
|
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 sourceType = this.typeUtils.getType(element);
|
||||||
String description = this.typeUtils.getJavaDoc(field);
|
String description = this.typeUtils.getJavaDoc(field);
|
||||||
Object defaultValue = fieldValues.get(name);
|
Object defaultValue = fieldValues.get(name);
|
||||||
|
|
@ -198,6 +205,48 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processLombokTypes(String prefix, TypeElement element,
|
||||||
|
TypeElementMembers members, Map<String, Object> fieldValues) {
|
||||||
|
for (Map.Entry<String, VariableElement> 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,
|
private void processNestedTypes(String prefix, TypeElement element,
|
||||||
TypeElementMembers members) {
|
TypeElementMembers members) {
|
||||||
for (Map.Entry<String, ExecutableElement> entry : members.getPublicGetters()
|
for (Map.Entry<String, ExecutableElement> entry : members.getPublicGetters()
|
||||||
|
|
@ -223,7 +272,7 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
|
||||||
|
|
||||||
private boolean isNested(Element returnType, VariableElement field,
|
private boolean isNested(Element returnType, VariableElement field,
|
||||||
TypeElement element) {
|
TypeElement element) {
|
||||||
if (getAnnotation(field, nestedConfigurationPropertyAnnotation()) != null) {
|
if (hasAnnotation(field, nestedConfigurationPropertyAnnotation())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return this.typeUtils.isEnclosedIn(returnType, element)
|
return this.typeUtils.isEnclosedIn(returnType, element)
|
||||||
|
|
@ -231,7 +280,11 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasDeprecateAnnotation(Element element) {
|
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) {
|
private AnnotationMirror getAnnotation(Element element, String type) {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package org.springframework.boot.configurationprocessor;
|
package org.springframework.boot.configurationprocessor;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||||
import javax.annotation.processing.SupportedSourceVersion;
|
import javax.annotation.processing.SupportedSourceVersion;
|
||||||
import javax.lang.model.SourceVersion;
|
import javax.lang.model.SourceVersion;
|
||||||
|
|
@ -25,7 +24,11 @@ import javax.lang.model.SourceVersion;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
|
||||||
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
|
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.EmptyTypeMethodConfig;
|
||||||
import org.springframework.boot.configurationsample.method.InvalidMethodConfig;
|
import org.springframework.boot.configurationsample.method.InvalidMethodConfig;
|
||||||
import org.springframework.boot.configurationsample.method.MethodAndClassConfig;
|
import org.springframework.boot.configurationsample.method.MethodAndClassConfig;
|
||||||
|
|
@ -286,6 +289,42 @@ public class ConfigurationMetadataAnnotationProcessorTests {
|
||||||
assertThat(metadata, not(containsProperty("excluded.writer-array")));
|
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 {
|
private ConfigurationMetadata compile(Class<?>... types) throws IOException {
|
||||||
TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor();
|
TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor();
|
||||||
new TestCompiler(this.temporaryFolder).getTask(types).call(processor);
|
new TestCompiler(this.temporaryFolder).getTask(types).call(processor);
|
||||||
|
|
|
||||||
|
|
@ -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<String> items = new ArrayList<String>();
|
||||||
|
|
||||||
|
// Should be ignored if no annotation is set
|
||||||
|
private String ignored;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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<String> items = new ArrayList<String>();
|
||||||
|
|
||||||
|
private final String ignored = "foo";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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<String> items = new ArrayList<String>();
|
||||||
|
|
||||||
|
private final String ignored = "foo";
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue