From c94a7dfa042f7c028f08d0898860a8b0d5735cb3 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 1 Jul 2019 17:01:07 -0700 Subject: [PATCH] Allow binding to package private methods Closes gh-17394 --- .../properties/bind/JavaBeanBinder.java | 6 ++--- .../properties/bind/ValueObjectBinder.java | 13 ++++++++-- .../properties/bind/JavaBeanBinderTests.java | 23 ++++++++++++++++++ .../bind/ValueObjectBinderTests.java | 24 +++++++++++++++++++ 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/JavaBeanBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/JavaBeanBinder.java index 689a00ba29..b6b112cdeb 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/JavaBeanBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/JavaBeanBinder.java @@ -149,9 +149,9 @@ class JavaBeanBinder implements DataObjectBinder { private boolean isCandidate(Method method) { int modifiers = method.getModifiers(); - return Modifier.isPublic(modifiers) && !Modifier.isAbstract(modifiers) && !Modifier.isStatic(modifiers) - && !Object.class.equals(method.getDeclaringClass()) - && !Class.class.equals(method.getDeclaringClass()); + return !Modifier.isPrivate(modifiers) && !Modifier.isProtected(modifiers) && !Modifier.isAbstract(modifiers) + && !Modifier.isStatic(modifiers) && !Object.class.equals(method.getDeclaringClass()) + && !Class.class.equals(method.getDeclaringClass()) && method.getName().indexOf('$') == -1; } private void addMethodIfPossible(Method method, String prefix, int parameterCount, diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java index 7fdb4b53d4..3bd2244b40 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java @@ -192,8 +192,17 @@ class ValueObjectBinder implements DataObjectBinder { @SuppressWarnings("unchecked") static ValueObject get(Class type) { - Constructor[] constructors = type.getDeclaredConstructors(); - return (constructors.length != 1) ? null : get((Constructor) constructors[0]); + Constructor constructor = null; + for (Constructor candidate : type.getDeclaredConstructors()) { + int modifiers = candidate.getModifiers(); + if (!Modifier.isPrivate(modifiers) && !Modifier.isProtected(modifiers)) { + if (constructor != null) { + return null; + } + constructor = candidate; + } + } + return get((Constructor) constructor); } static DefaultValueObject get(Constructor constructor) { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/JavaBeanBinderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/JavaBeanBinderTests.java index 3067e87c31..da795da6dc 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/JavaBeanBinderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/JavaBeanBinderTests.java @@ -529,6 +529,15 @@ class JavaBeanBinderTests { assertThat(result.getNested().getBar()).isEqualTo(456); } + @Test + void bindWhenHasPackagePrivateSetterShouldBind() { + MockConfigurationPropertySource source = new MockConfigurationPropertySource(); + source.put("foo.property", "test"); + this.sources.add(source); + PackagePrivateSetterBean bean = this.binder.bind("foo", Bindable.of(PackagePrivateSetterBean.class)).get(); + assertThat(bean.getProperty()).isEqualTo("test"); + } + public static class ExampleValueBean { private int intValue; @@ -1011,4 +1020,18 @@ class JavaBeanBinderTests { } + public static class PackagePrivateSetterBean { + + private String property; + + public String getProperty() { + return this.property; + } + + void setProperty(String property) { + this.property = property; + } + + } + } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java index 322ae2d54d..62630de903 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java @@ -166,6 +166,16 @@ class ValueObjectBinderTests { assertThat(bean.getDate().toString()).isEqualTo("2019-05-10"); } + @Test + void bindToClassWhenHasPackagePrivateConstructorShouldBind() { + MockConfigurationPropertySource source = new MockConfigurationPropertySource(); + source.put("foo.property", "test"); + this.sources.add(source); + ExamplePackagePrivateConstructorBean bound = this.binder + .bind("foo", Bindable.of(ExamplePackagePrivateConstructorBean.class)).get(); + assertThat(bound.getProperty()).isEqualTo("test"); + } + @Test void createShouldReturnCreatedValue() { ExampleValueBean value = this.binder.bindOrCreate("foo", Bindable.of(ExampleValueBean.class)); @@ -370,4 +380,18 @@ class ValueObjectBinderTests { } + public static class ExamplePackagePrivateConstructorBean { + + private final String property; + + ExamplePackagePrivateConstructorBean(String property) { + this.property = property; + } + + public String getProperty() { + return this.property; + } + + } + }