Add @NestedConfigurationProperty meta-data support

Add a @NestedConfigurationProperty annotation which can be used to
customize how configuration mete-data is generated.

Prior to this commit only inner-classes where considered nested
(see Tomcat in ServerProperties). Using this new annotation, the Ssl
property in ServerProperties can be detected as well.

See gh-1001
pull/1815/head
Stephane Nicoll 10 years ago committed by Phillip Webb
parent fbf8f56a97
commit a46396f691

@ -38,6 +38,7 @@ import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomize
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.util.StringUtils;
/**
@ -60,6 +61,7 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
private String contextPath;
@NestedConfigurationProperty
private Ssl ssl;
@NotNull

@ -64,6 +64,9 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
static final String CONFIGURATION_PROPERTIES_ANNOTATION = "org.springframework.boot."
+ "context.properties.ConfigurationProperties";
static final String NESTED_CONFIGURATION_PROPERTY_ANNOTATION = "org.springframework.boot."
+ "context.properties.NestedConfigurationProperty";
private ConfigurationMetadata metadata;
private TypeUtils typeUtils;
@ -74,6 +77,10 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
return CONFIGURATION_PROPERTIES_ANNOTATION;
}
protected String nestedConfigurationPropertyAnnotation() {
return NESTED_CONFIGURATION_PROPERTY_ANNOTATION;
}
@Override
public synchronized void init(ProcessingEnvironment env) {
super.init(env);
@ -166,8 +173,11 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
ExecutableElement getter = entry.getValue();
ExecutableElement setter = members.getPublicSetters().get(name);
VariableElement field = members.getFields().get(name);
if (setter != null
|| this.typeUtils.isCollectionOrMap(getter.getReturnType())) {
boolean isNested = getAnnotation(field,
nestedConfigurationPropertyAnnotation()) != null;
boolean isCollection = this.typeUtils.isCollectionOrMap(getter
.getReturnType());
if (!isNested && (setter != null || isCollection)) {
String dataType = this.typeUtils.getType(getter.getReturnType());
String sourceType = this.typeUtils.getType(element);
String description = this.typeUtils.getJavaDoc(field);
@ -182,17 +192,21 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
TypeElementMembers members) {
for (Map.Entry<String, ExecutableElement> entry : members.getPublicGetters()
.entrySet()) {
String name = entry.getKey();
ExecutableElement getter = entry.getValue();
VariableElement field = members.getFields().get(name);
Element returnType = this.processingEnv.getTypeUtils().asElement(
getter.getReturnType());
AnnotationMirror annotation = getAnnotation(getter,
configurationPropertiesAnnotation());
boolean isNested = getAnnotation(field,
nestedConfigurationPropertyAnnotation()) != null;
if (returnType != null && returnType instanceof TypeElement
&& annotation == null) {
TypeElement returns = (TypeElement) returnType;
if (this.typeUtils.isEnclosedIn(returnType, element)) {
String nestedPrefix = ConfigurationMetadata.nestedPrefix(prefix,
entry.getKey());
if (this.typeUtils.isEnclosedIn(returnType, element) || isNested) {
String nestedPrefix = ConfigurationMetadata
.nestedPrefix(prefix, name);
this.metadata.add(ItemMetadata.newGroup(nestedPrefix,
this.typeUtils.getType(returns),
this.typeUtils.getType(element), getter.toString()));
@ -203,9 +217,11 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
}
private AnnotationMirror getAnnotation(Element element, String type) {
for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
if (type.equals(annotation.getAnnotationType().toString())) {
return annotation;
if (element != null) {
for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
if (type.equals(annotation.getAnnotationType().toString())) {
return annotation;
}
}
}
return null;

@ -39,6 +39,7 @@ import org.springframework.boot.configurationsample.simple.SimpleTypeProperties;
import org.springframework.boot.configurationsample.specific.InnerClassAnnotatedGetterConfig;
import org.springframework.boot.configurationsample.specific.InnerClassProperties;
import org.springframework.boot.configurationsample.specific.InnerClassRootConfig;
import org.springframework.boot.configurationsample.specific.SimplePojo;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
@ -238,11 +239,8 @@ public class ConfigurationMetadataAnnotationProcessorTests {
.fromSource(InnerClassProperties.class));
assertThat(metadata, containsProperty("config.the-second.name"));
assertThat(metadata, containsProperty("config.the-second.bar.name"));
assertThat(
metadata,
containsGroup("config.third").ofType(
InnerClassProperties.SimplePojo.class).fromSource(
InnerClassProperties.class));
assertThat(metadata, containsGroup("config.third").ofType(SimplePojo.class)
.fromSource(InnerClassProperties.class));
assertThat(metadata, containsProperty("config.third.value"));
}
@ -267,6 +265,8 @@ public class ConfigurationMetadataAnnotationProcessorTests {
static final String CONFIGURATION_PROPERTIES_ANNOTATION = "org.springframework.boot.configurationsample.ConfigurationProperties";
static final String NESTED_CONFIGURATION_PROPERTY_ANNOTATION = "org.springframework.boot.configurationsample.NestedConfigurationProperty";
private ConfigurationMetadata metadata;
@Override
@ -274,6 +274,11 @@ public class ConfigurationMetadataAnnotationProcessorTests {
return CONFIGURATION_PROPERTIES_ANNOTATION;
}
@Override
protected String nestedConfigurationPropertyAnnotation() {
return NESTED_CONFIGURATION_PROPERTY_ANNOTATION;
}
@Override
protected void writeMetaData(ConfigurationMetadata metadata) {
this.metadata = metadata;

@ -0,0 +1,38 @@
/*
* 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;
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 @NestedConfigurationProperty} for testing (removes
* the need for a dependency on the real annotation).
*
* @author Stephane Nicoll
* @author Phillip Webb
* @since 1.2.0
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NestedConfigurationProperty {
}

@ -17,6 +17,7 @@
package org.springframework.boot.configurationsample.specific;
import org.springframework.boot.configurationsample.ConfigurationProperties;
import org.springframework.boot.configurationsample.NestedConfigurationProperty;
/**
* Demonstrate the auto-detection of a inner config classes.
@ -30,6 +31,7 @@ public class InnerClassProperties {
private Foo second = new Foo();
@NestedConfigurationProperty
private final SimplePojo third = new SimplePojo();
public Foo getFirst() {
@ -81,9 +83,4 @@ public class InnerClassProperties {
}
public static class SimplePojo extends
org.springframework.boot.configurationsample.specific.SimplePojo {
}
}

@ -0,0 +1,40 @@
/*
* 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.context.properties;
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;
/**
* Indicates that a field in a {@link ConfigurationProperties} object should be treated as
* if it were a nested type. This annotation has no bearing on the actual binding
* processes, but it is used by the {@code spring-boot-configuration-processor} as a hint
* that a field is not bound as a single value.
*
* @author Stephane Nicoll
* @author Phillip Webb
* @since 1.2.0
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NestedConfigurationProperty {
}
Loading…
Cancel
Save