Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
S
spring-boot
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DEMO
spring-boot
Commits
b6ff0b7c
Commit
b6ff0b7c
authored
Nov 13, 2019
by
Madhura Bhave
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support constructor binding on 3rd party classes
Closes gh-18935
parent
7d540543
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
97 additions
and
14 deletions
+97
-14
ConfigurationPropertiesBean.java
.../boot/context/properties/ConfigurationPropertiesBean.java
+1
-1
ConfigurationPropertiesBindConstructorProvider.java
...rties/ConfigurationPropertiesBindConstructorProvider.java
+4
-4
BindConstructorProvider.java
...boot/context/properties/bind/BindConstructorProvider.java
+3
-1
Binder.java
.../springframework/boot/context/properties/bind/Binder.java
+14
-0
DefaultBindConstructorProvider.java
...ntext/properties/bind/DefaultBindConstructorProvider.java
+1
-1
ValueObjectBinder.java
...ework/boot/context/properties/bind/ValueObjectBinder.java
+8
-4
ConfigurationPropertiesBeanTests.java
.../context/properties/ConfigurationPropertiesBeanTests.java
+1
-1
ConfigurationPropertiesTests.java
...boot/context/properties/ConfigurationPropertiesTests.java
+61
-0
JavaBeanBinderTests.java
...ork/boot/context/properties/bind/JavaBeanBinderTests.java
+2
-1
ValueObjectBinderTests.java
.../boot/context/properties/bind/ValueObjectBinderTests.java
+2
-1
No files found.
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBean.java
View file @
b6ff0b7c
...
@@ -308,7 +308,7 @@ public final class ConfigurationPropertiesBean {
...
@@ -308,7 +308,7 @@ public final class ConfigurationPropertiesBean {
VALUE_OBJECT
;
VALUE_OBJECT
;
static
BindMethod
forType
(
Class
<?>
type
)
{
static
BindMethod
forType
(
Class
<?>
type
)
{
return
(
ConfigurationPropertiesBindConstructorProvider
.
INSTANCE
.
getBindConstructor
(
type
)
!=
null
)
return
(
ConfigurationPropertiesBindConstructorProvider
.
INSTANCE
.
getBindConstructor
(
type
,
false
)
!=
null
)
?
VALUE_OBJECT
:
JAVA_BEAN
;
?
VALUE_OBJECT
:
JAVA_BEAN
;
}
}
...
...
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindConstructorProvider.java
View file @
b6ff0b7c
...
@@ -37,16 +37,16 @@ class ConfigurationPropertiesBindConstructorProvider implements BindConstructorP
...
@@ -37,16 +37,16 @@ class ConfigurationPropertiesBindConstructorProvider implements BindConstructorP
static
final
ConfigurationPropertiesBindConstructorProvider
INSTANCE
=
new
ConfigurationPropertiesBindConstructorProvider
();
static
final
ConfigurationPropertiesBindConstructorProvider
INSTANCE
=
new
ConfigurationPropertiesBindConstructorProvider
();
@Override
@Override
public
Constructor
<?>
getBindConstructor
(
Bindable
<?>
bindable
)
{
public
Constructor
<?>
getBindConstructor
(
Bindable
<?>
bindable
,
boolean
isNestedConstructorBinding
)
{
return
getBindConstructor
(
bindable
.
getType
().
resolve
());
return
getBindConstructor
(
bindable
.
getType
().
resolve
()
,
isNestedConstructorBinding
);
}
}
Constructor
<?>
getBindConstructor
(
Class
<?>
type
)
{
Constructor
<?>
getBindConstructor
(
Class
<?>
type
,
boolean
isNestedConstructorBinding
)
{
if
(
type
==
null
)
{
if
(
type
==
null
)
{
return
null
;
return
null
;
}
}
Constructor
<?>
constructor
=
findConstructorBindingAnnotatedConstructor
(
type
);
Constructor
<?>
constructor
=
findConstructorBindingAnnotatedConstructor
(
type
);
if
(
constructor
==
null
&&
isConstructorBindingAnnotatedType
(
type
))
{
if
(
constructor
==
null
&&
(
isConstructorBindingAnnotatedType
(
type
)
||
isNestedConstructorBinding
))
{
constructor
=
deduceBindConstructor
(
type
);
constructor
=
deduceBindConstructor
(
type
);
}
}
return
constructor
;
return
constructor
;
...
...
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/BindConstructorProvider.java
View file @
b6ff0b7c
...
@@ -37,8 +37,10 @@ public interface BindConstructorProvider {
...
@@ -37,8 +37,10 @@ public interface BindConstructorProvider {
* Return the bind constructor to use for the given bindable, or {@code null} if
* Return the bind constructor to use for the given bindable, or {@code null} if
* constructor binding is not supported.
* constructor binding is not supported.
* @param bindable the bindable to check
* @param bindable the bindable to check
* @param isNestedConstructorBinding if this binding is nested within a constructor
* binding
* @return the bind constructor or {@code null}
* @return the bind constructor or {@code null}
*/
*/
Constructor
<?>
getBindConstructor
(
Bindable
<?>
bindable
);
Constructor
<?>
getBindConstructor
(
Bindable
<?>
bindable
,
boolean
isNestedConstructorBinding
);
}
}
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java
View file @
b6ff0b7c
...
@@ -522,6 +522,8 @@ public class Binder {
...
@@ -522,6 +522,8 @@ public class Binder {
private
final
Deque
<
Class
<?>>
dataObjectBindings
=
new
ArrayDeque
<>();
private
final
Deque
<
Class
<?>>
dataObjectBindings
=
new
ArrayDeque
<>();
private
final
Deque
<
Class
<?>>
constructorBindings
=
new
ArrayDeque
<>();
private
ConfigurationProperty
configurationProperty
;
private
ConfigurationProperty
configurationProperty
;
Context
()
{
Context
()
{
...
@@ -582,6 +584,18 @@ public class Binder {
...
@@ -582,6 +584,18 @@ public class Binder {
this
.
configurationProperty
=
null
;
this
.
configurationProperty
=
null
;
}
}
void
pushConstructorBoundTypes
(
Class
<?>
value
)
{
this
.
constructorBindings
.
push
(
value
);
}
boolean
isNestedConstructorBinding
()
{
return
!
this
.
constructorBindings
.
isEmpty
();
}
void
popConstructorBoundTypes
()
{
this
.
constructorBindings
.
pop
();
}
PlaceholdersResolver
getPlaceholdersResolver
()
{
PlaceholdersResolver
getPlaceholdersResolver
()
{
return
Binder
.
this
.
placeholdersResolver
;
return
Binder
.
this
.
placeholdersResolver
;
}
}
...
...
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/DefaultBindConstructorProvider.java
View file @
b6ff0b7c
...
@@ -30,7 +30,7 @@ import org.springframework.core.KotlinDetector;
...
@@ -30,7 +30,7 @@ import org.springframework.core.KotlinDetector;
class
DefaultBindConstructorProvider
implements
BindConstructorProvider
{
class
DefaultBindConstructorProvider
implements
BindConstructorProvider
{
@Override
@Override
public
Constructor
<?>
getBindConstructor
(
Bindable
<?>
bindable
)
{
public
Constructor
<?>
getBindConstructor
(
Bindable
<?>
bindable
,
boolean
isNestedConstructorBinding
)
{
Class
<?>
type
=
bindable
.
getType
().
resolve
();
Class
<?>
type
=
bindable
.
getType
().
resolve
();
if
(
bindable
.
getValue
()
!=
null
||
type
==
null
)
{
if
(
bindable
.
getValue
()
!=
null
||
type
==
null
)
{
return
null
;
return
null
;
...
...
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java
View file @
b6ff0b7c
...
@@ -53,10 +53,11 @@ class ValueObjectBinder implements DataObjectBinder {
...
@@ -53,10 +53,11 @@ class ValueObjectBinder implements DataObjectBinder {
@Override
@Override
public
<
T
>
T
bind
(
ConfigurationPropertyName
name
,
Bindable
<
T
>
target
,
Binder
.
Context
context
,
public
<
T
>
T
bind
(
ConfigurationPropertyName
name
,
Bindable
<
T
>
target
,
Binder
.
Context
context
,
DataObjectPropertyBinder
propertyBinder
)
{
DataObjectPropertyBinder
propertyBinder
)
{
ValueObject
<
T
>
valueObject
=
ValueObject
.
get
(
target
,
this
.
constructorProvider
);
ValueObject
<
T
>
valueObject
=
ValueObject
.
get
(
target
,
this
.
constructorProvider
,
context
);
if
(
valueObject
==
null
)
{
if
(
valueObject
==
null
)
{
return
null
;
return
null
;
}
}
context
.
pushConstructorBoundTypes
(
target
.
getType
().
resolve
());
List
<
ConstructorParameter
>
parameters
=
valueObject
.
getConstructorParameters
();
List
<
ConstructorParameter
>
parameters
=
valueObject
.
getConstructorParameters
();
List
<
Object
>
args
=
new
ArrayList
<>(
parameters
.
size
());
List
<
Object
>
args
=
new
ArrayList
<>(
parameters
.
size
());
boolean
bound
=
false
;
boolean
bound
=
false
;
...
@@ -67,12 +68,13 @@ class ValueObjectBinder implements DataObjectBinder {
...
@@ -67,12 +68,13 @@ class ValueObjectBinder implements DataObjectBinder {
args
.
add
(
arg
);
args
.
add
(
arg
);
}
}
context
.
clearConfigurationProperty
();
context
.
clearConfigurationProperty
();
context
.
popConstructorBoundTypes
();
return
bound
?
valueObject
.
instantiate
(
args
)
:
null
;
return
bound
?
valueObject
.
instantiate
(
args
)
:
null
;
}
}
@Override
@Override
public
<
T
>
T
create
(
Bindable
<
T
>
target
,
Binder
.
Context
context
)
{
public
<
T
>
T
create
(
Bindable
<
T
>
target
,
Binder
.
Context
context
)
{
ValueObject
<
T
>
valueObject
=
ValueObject
.
get
(
target
,
this
.
constructorProvider
);
ValueObject
<
T
>
valueObject
=
ValueObject
.
get
(
target
,
this
.
constructorProvider
,
context
);
if
(
valueObject
==
null
)
{
if
(
valueObject
==
null
)
{
return
null
;
return
null
;
}
}
...
@@ -104,12 +106,14 @@ class ValueObjectBinder implements DataObjectBinder {
...
@@ -104,12 +106,14 @@ class ValueObjectBinder implements DataObjectBinder {
abstract
List
<
ConstructorParameter
>
getConstructorParameters
();
abstract
List
<
ConstructorParameter
>
getConstructorParameters
();
@SuppressWarnings
(
"unchecked"
)
@SuppressWarnings
(
"unchecked"
)
static
<
T
>
ValueObject
<
T
>
get
(
Bindable
<
T
>
bindable
,
BindConstructorProvider
constructorProvider
)
{
static
<
T
>
ValueObject
<
T
>
get
(
Bindable
<
T
>
bindable
,
BindConstructorProvider
constructorProvider
,
Binder
.
Context
context
)
{
Class
<
T
>
type
=
(
Class
<
T
>)
bindable
.
getType
().
resolve
();
Class
<
T
>
type
=
(
Class
<
T
>)
bindable
.
getType
().
resolve
();
if
(
type
==
null
||
type
.
isEnum
()
||
Modifier
.
isAbstract
(
type
.
getModifiers
()))
{
if
(
type
==
null
||
type
.
isEnum
()
||
Modifier
.
isAbstract
(
type
.
getModifiers
()))
{
return
null
;
return
null
;
}
}
Constructor
<?>
bindConstructor
=
constructorProvider
.
getBindConstructor
(
bindable
);
Constructor
<?>
bindConstructor
=
constructorProvider
.
getBindConstructor
(
bindable
,
context
.
isNestedConstructorBinding
());
if
(
bindConstructor
==
null
)
{
if
(
bindConstructor
==
null
)
{
return
null
;
return
null
;
}
}
...
...
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanTests.java
View file @
b6ff0b7c
...
@@ -213,7 +213,7 @@ class ConfigurationPropertiesBeanTests {
...
@@ -213,7 +213,7 @@ class ConfigurationPropertiesBeanTests {
assertThat
(
target
.
getType
()).
isEqualTo
(
ResolvableType
.
forClass
(
ConstructorBindingOnConstructor
.
class
));
assertThat
(
target
.
getType
()).
isEqualTo
(
ResolvableType
.
forClass
(
ConstructorBindingOnConstructor
.
class
));
assertThat
(
target
.
getValue
()).
isNull
();
assertThat
(
target
.
getValue
()).
isNull
();
assertThat
(
ConfigurationPropertiesBindConstructorProvider
.
INSTANCE
assertThat
(
ConfigurationPropertiesBindConstructorProvider
.
INSTANCE
.
getBindConstructor
(
ConstructorBindingOnConstructor
.
class
)).
isNotNull
();
.
getBindConstructor
(
ConstructorBindingOnConstructor
.
class
,
false
)).
isNotNull
();
}
}
@Test
@Test
...
...
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java
View file @
b6ff0b7c
...
@@ -907,6 +907,18 @@ class ConfigurationPropertiesTests {
...
@@ -907,6 +907,18 @@ class ConfigurationPropertiesTests {
load
(
TestConfiguration
.
class
);
load
(
TestConfiguration
.
class
);
}
}
@Test
void
loadWhenConstructorBindingWithOuterClassDeducedConstructorBound
()
{
MutablePropertySources
sources
=
this
.
context
.
getEnvironment
().
getPropertySources
();
Map
<
String
,
Object
>
source
=
new
HashMap
<>();
source
.
put
(
"test.nested.outer.age"
,
"5"
);
sources
.
addLast
(
new
MapPropertySource
(
"test"
,
source
));
load
(
ConstructorBindingWithOuterClassConstructorBoundConfiguration
.
class
);
ConstructorBindingWithOuterClassConstructorBoundProperties
bean
=
this
.
context
.
getBean
(
ConstructorBindingWithOuterClassConstructorBoundProperties
.
class
);
assertThat
(
bean
.
getNested
().
getOuter
().
getAge
()).
isEqualTo
(
5
);
}
private
AnnotationConfigApplicationContext
load
(
Class
<?>
configuration
,
String
...
inlinedProperties
)
{
private
AnnotationConfigApplicationContext
load
(
Class
<?>
configuration
,
String
...
inlinedProperties
)
{
return
load
(
new
Class
<?>[]
{
configuration
},
inlinedProperties
);
return
load
(
new
Class
<?>[]
{
configuration
},
inlinedProperties
);
}
}
...
@@ -2126,6 +2138,55 @@ class ConfigurationPropertiesTests {
...
@@ -2126,6 +2138,55 @@ class ConfigurationPropertiesTests {
}
}
@ConfigurationProperties
(
"test"
)
@ConstructorBinding
static
class
ConstructorBindingWithOuterClassConstructorBoundProperties
{
private
final
Nested
nested
;
ConstructorBindingWithOuterClassConstructorBoundProperties
(
Nested
nested
)
{
this
.
nested
=
nested
;
}
Nested
getNested
()
{
return
this
.
nested
;
}
static
class
Nested
{
private
Outer
outer
;
Outer
getOuter
()
{
return
this
.
outer
;
}
void
setOuter
(
Outer
nested
)
{
this
.
outer
=
nested
;
}
}
}
static
class
Outer
{
private
int
age
;
Outer
(
int
age
)
{
this
.
age
=
age
;
}
int
getAge
()
{
return
this
.
age
;
}
}
@EnableConfigurationProperties
(
ConstructorBindingWithOuterClassConstructorBoundProperties
.
class
)
static
class
ConstructorBindingWithOuterClassConstructorBoundConfiguration
{
}
@ConfigurationProperties
(
"test"
)
@ConfigurationProperties
(
"test"
)
static
class
MultiConstructorConfigurationListProperties
{
static
class
MultiConstructorConfigurationListProperties
{
...
...
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/JavaBeanBinderTests.java
View file @
b6ff0b7c
...
@@ -339,7 +339,8 @@ class JavaBeanBinderTests {
...
@@ -339,7 +339,8 @@ class JavaBeanBinderTests {
@Test
@Test
void
bindToInstanceWhenNoDefaultConstructorShouldBind
()
{
void
bindToInstanceWhenNoDefaultConstructorShouldBind
()
{
Binder
binder
=
new
Binder
(
this
.
sources
,
null
,
null
,
null
,
null
,
(
type
)
->
null
);
Binder
binder
=
new
Binder
(
this
.
sources
,
null
,
null
,
null
,
null
,
(
bindable
,
isNestedConstructorBinding
)
->
null
);
MockConfigurationPropertySource
source
=
new
MockConfigurationPropertySource
();
MockConfigurationPropertySource
source
=
new
MockConfigurationPropertySource
();
source
.
put
(
"foo.value"
,
"bar"
);
source
.
put
(
"foo.value"
,
"bar"
);
this
.
sources
.
add
(
source
);
this
.
sources
.
add
(
source
);
...
...
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java
View file @
b6ff0b7c
...
@@ -103,7 +103,8 @@ class ValueObjectBinderTests {
...
@@ -103,7 +103,8 @@ class ValueObjectBinderTests {
this
.
sources
.
add
(
source
);
this
.
sources
.
add
(
source
);
Constructor
<?>[]
constructors
=
MultipleConstructorsBean
.
class
.
getDeclaredConstructors
();
Constructor
<?>[]
constructors
=
MultipleConstructorsBean
.
class
.
getDeclaredConstructors
();
Constructor
<?>
constructor
=
(
constructors
[
0
].
getParameterCount
()
==
1
)
?
constructors
[
0
]
:
constructors
[
1
];
Constructor
<?>
constructor
=
(
constructors
[
0
].
getParameterCount
()
==
1
)
?
constructors
[
0
]
:
constructors
[
1
];
Binder
binder
=
new
Binder
(
this
.
sources
,
null
,
null
,
null
,
null
,
(
type
)
->
constructor
);
Binder
binder
=
new
Binder
(
this
.
sources
,
null
,
null
,
null
,
null
,
(
bindable
,
isNestedConstructorBinding
)
->
constructor
);
MultipleConstructorsBean
bound
=
binder
.
bind
(
"foo"
,
Bindable
.
of
(
MultipleConstructorsBean
.
class
)).
get
();
MultipleConstructorsBean
bound
=
binder
.
bind
(
"foo"
,
Bindable
.
of
(
MultipleConstructorsBean
.
class
)).
get
();
assertThat
(
bound
.
getIntValue
()).
isEqualTo
(
12
);
assertThat
(
bound
.
getIntValue
()).
isEqualTo
(
12
);
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment