Fix detection of property setter

Previously, the meta-data annotation processor was taking the first
setter that match the property name it has to handle. Contrary to
getters that are enforced by a return type (no argument), multiple
setter candidates may exist.

If a property's type got narrowed over time, the original setter may
have been marked as Deprecated. As the annotation processor takes the
first setter that matches based on the name only, it may pick up the
deprecated one and therefore mark the property as being (wrongly)
deprecatede in the meta-data.

It turns out that checking for the actual type of the setter parameter
brought a side effect: some primitive properties may use the primitive
or the Wrapper counter part. This commit not only look at the proper
setter based on the type but also fallback on the wrapper (or) primitive
if necessary.

Closes gh-4338
This commit is contained in:
Stephane Nicoll
2015-10-30 14:39:43 +01:00
parent 5ed7156061
commit 0b326035b0
7 changed files with 229 additions and 11 deletions

View File

@@ -51,7 +51,9 @@ import org.springframework.boot.configurationsample.simple.SimpleCollectionPrope
import org.springframework.boot.configurationsample.simple.SimplePrefixValueProperties;
import org.springframework.boot.configurationsample.simple.SimpleProperties;
import org.springframework.boot.configurationsample.simple.SimpleTypeProperties;
import org.springframework.boot.configurationsample.specific.BoxingPojo;
import org.springframework.boot.configurationsample.specific.BuilderPojo;
import org.springframework.boot.configurationsample.specific.DeprecatedUnrelatedMethodPojo;
import org.springframework.boot.configurationsample.specific.ExcludedTypesPojo;
import org.springframework.boot.configurationsample.specific.InnerClassAnnotatedGetterConfig;
import org.springframework.boot.configurationsample.specific.InnerClassProperties;
@@ -197,6 +199,30 @@ public class ConfigurationMetadataAnnotationProcessorTests {
.withDeprecation("renamed", "singledeprecated.new-name"));
}
@Test
public void deprecatedOnUnrelatedSetter() throws Exception {
Class<?> type = DeprecatedUnrelatedMethodPojo.class;
ConfigurationMetadata metadata = compile(type);
assertThat(metadata, containsGroup("not.deprecated").fromSource(type));
assertThat(metadata, containsProperty("not.deprecated.counter", Integer.class)
.withNoDeprecation()
.fromSource(type));
assertThat(metadata, containsProperty("not.deprecated.flag", Boolean.class)
.withNoDeprecation()
.fromSource(type));
}
@Test
public void boxingOnSetter() throws IOException {
Class<?> type = BoxingPojo.class;
ConfigurationMetadata metadata = compile(type);
assertThat(metadata, containsGroup("boxing").fromSource(type));
assertThat(metadata, containsProperty("boxing.flag", Boolean.class)
.fromSource(type));
assertThat(metadata, containsProperty("boxing.counter", Integer.class)
.fromSource(type));
}
@Test
public void parseCollectionConfig() throws Exception {
ConfigurationMetadata metadata = compile(SimpleCollectionProperties.class);
@@ -343,7 +369,9 @@ public class ConfigurationMetadataAnnotationProcessorTests {
assertThat(metadata,
containsProperty("simple.flag", Boolean.class)
.fromSource(SimpleProperties.class)
.withDescription("A simple flag.").withDefaultValue(is(true)));
.withDescription("A simple flag.")
.withDeprecation(null, null)
.withDefaultValue(is(true)));
assertThat(metadata.getItems().size(), is(4));
}

View File

@@ -125,6 +125,9 @@ public final class ConfigurationMetadataMatchers {
&& !this.description.equals(itemMetadata.getDescription())) {
return false;
}
if (this.deprecation == null && itemMetadata.getDeprecation() != null) {
return false;
}
if (this.deprecation != null
&& !this.deprecation.equals(itemMetadata.getDeprecation())) {
return false;
@@ -200,6 +203,11 @@ public final class ConfigurationMetadataMatchers {
new ItemDeprecation(reason, replacement));
}
public ContainsItemMatcher withNoDeprecation() {
return new ContainsItemMatcher(this.itemType, this.name, this.type,
this.sourceType, this.description, this.defaultValue, null);
}
private ItemMetadata getFirstItemWithName(ConfigurationMetadata metadata,
String name) {
for (ItemMetadata item : metadata.getItems()) {

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2012-2015 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.specific;
import org.springframework.boot.configurationsample.ConfigurationProperties;
/**
* Demonstrate the use of boxing/unboxing. Even if the type does not
* strictly match, it should still be detected.
*
* @author Stephane Nicoll
*/
@ConfigurationProperties("boxing")
public class BoxingPojo {
private boolean flag;
private Integer counter;
public boolean isFlag() {
return this.flag;
}
// Setter use Boolean
public void setFlag(Boolean flag) {
this.flag = flag;
}
public Integer getCounter() {
return this.counter;
}
// Setter use int
public void setCounter(int counter) {
this.counter = counter;
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2012-2015 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.specific;
import org.springframework.boot.configurationsample.ConfigurationProperties;
/**
* Demonstrate that an unrelated setter is not taken into account
* to detect the deprecated flag.
*
* @author Stephane Nicoll
*/
@ConfigurationProperties("not.deprecated")
public class DeprecatedUnrelatedMethodPojo {
private Integer counter;
private boolean flag;
public Integer getCounter() {
return this.counter;
}
public void setCounter(Integer counter) {
this.counter = counter;
}
@Deprecated
public void setCounter(String counterAsString) {
this.counter = Integer.valueOf(counterAsString);
}
public boolean isFlag() {
return this.flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
@Deprecated
public void setFlag(Boolean flag) {
this.flag = flag;
}
}