Ignore static and abstract accessors

This commit updates the annotation processor and the binder to ignore
any static or abstract method that has the characteristics of a JavaBean
accessor. As a result, no property is generated for those (invalid)
accessor and no binding occurs on them either.

Closes gh-12390
This commit is contained in:
Stephane Nicoll 2018-03-07 16:14:06 +01:00
parent 72afdc676d
commit 7d1faa1c88
5 changed files with 111 additions and 3 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -21,6 +21,7 @@ import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
@ -97,7 +98,7 @@ class TypeElementMembers {
}
private void processMethod(ExecutableElement method) {
if (method.getModifiers().contains(Modifier.PUBLIC)) {
if (isPublic(method)) {
String name = method.getSimpleName().toString();
if (isGetter(method) && !this.publicGetters.containsKey(name)) {
this.publicGetters.put(getAccessorName(name), method);
@ -118,6 +119,13 @@ class TypeElementMembers {
}
}
private boolean isPublic(ExecutableElement method) {
Set<Modifier> modifiers = method.getModifiers();
return modifiers.contains(Modifier.PUBLIC)
&& !modifiers.contains(Modifier.ABSTRACT)
&& !modifiers.contains(Modifier.STATIC);
}
private ExecutableElement getMatchingSetter(List<ExecutableElement> candidates,
TypeMirror type) {
for (ExecutableElement candidate : candidates) {

View File

@ -81,6 +81,7 @@ import org.springframework.boot.configurationsample.specific.InnerClassRootConfi
import org.springframework.boot.configurationsample.specific.InvalidAccessorProperties;
import org.springframework.boot.configurationsample.specific.InvalidDoubleRegistrationProperties;
import org.springframework.boot.configurationsample.specific.SimplePojo;
import org.springframework.boot.configurationsample.specific.StaticAccessor;
import org.springframework.boot.configurationsample.specific.WildcardConfig;
import org.springframework.boot.testsupport.compiler.TestCompiler;
import org.springframework.util.FileCopyUtils;
@ -365,6 +366,18 @@ public class ConfigurationMetadataAnnotationProcessorTests {
.fromSource(AnnotatedGetter.class));
}
@Test
public void staticAccessor() {
ConfigurationMetadata metadata = compile(StaticAccessor.class);
assertThat(metadata)
.has(Metadata.withGroup("specific").fromSource(StaticAccessor.class));
assertThat(metadata).has(Metadata.withProperty("specific.counter",
Integer.class).fromSource(StaticAccessor.class).withDefaultValue(42));
assertThat(metadata).doesNotHave(Metadata.withProperty("specific.name",
String.class).fromSource(StaticAccessor.class));
assertThat(metadata.getItems()).hasSize(2);
}
@Test
public void innerClassRootConfig() {
ConfigurationMetadata metadata = compile(InnerClassRootConfig.class);

View File

@ -0,0 +1,49 @@
/*
* Copyright 2012-2018 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;
/**
* A property that is exposed by static accessors.
*
* @author Stephane Nicoll
*/
@ConfigurationProperties("specific")
public class StaticAccessor {
private static String name;
private int counter = 42;
public static String getName() {
return name;
}
public static void setName(String name) {
StaticAccessor.name = name;
}
public int getCounter() {
return this.counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
}

View File

@ -118,7 +118,9 @@ class JavaBeanBinder implements BeanBinder {
}
private boolean isCandidate(Method method) {
return Modifier.isPublic(method.getModifiers())
int modifiers = method.getModifiers();
return Modifier.isPublic(modifiers) && !Modifier.isAbstract(modifiers)
&& !Modifier.isStatic(modifiers)
&& !Object.class.equals(method.getDeclaringClass())
&& !Class.class.equals(method.getDeclaringClass());
}

View File

@ -486,6 +486,18 @@ public class JavaBeanBinderTests {
assertThat(bean.getName()).isEqualTo("something");
}
@Test
public void bindToClassShouldIgnoreStaticAccessors() {
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
source.put("foo.name", "invalid");
source.put("foo.counter", "42");
this.sources.add(source);
ExampleWithStaticAccessors bean = this.binder
.bind("foo", Bindable.of(ExampleWithStaticAccessors.class)).get();
assertThat(ExampleWithStaticAccessors.name).isNull();
assertThat(bean.getCounter()).isEqualTo(42);
}
public static class ExampleValueBean {
private int intValue;
@ -845,6 +857,30 @@ public class JavaBeanBinderTests {
}
public static class ExampleWithStaticAccessors {
private static String name;
private int counter;
public static String getName() {
return name;
}
public static void setName(String name) {
ExampleWithStaticAccessors.name = name;
}
public int getCounter() {
return this.counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
}
public enum ExampleEnum {
FOO_BAR,