Skip to content

Commit 91d14fa

Browse files
committed
Merge branch '3.4.x'
Closes gh-45553
2 parents fee8aa0 + bd40779 commit 91d14fa

File tree

5 files changed

+136
-1
lines changed

5 files changed

+136
-1
lines changed

spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/external-config.adoc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,14 @@ include-code::MyProperties[]
821821
In this setup, the presence of a single parameterized constructor implies that constructor binding should be used.
822822
This means that the binder will find a constructor with the parameters that you wish to have bound.
823823
If your class has multiple constructors, the javadoc:org.springframework.boot.context.properties.bind.ConstructorBinding[format=annotation] annotation can be used to specify which constructor to use for constructor binding.
824-
To opt out of constructor binding for a class with a single parameterized constructor, the constructor must be annotated with javadoc:org.springframework.beans.factory.annotation.Autowired[format=annotation] or made `private`.
824+
825+
To opt-out of constructor binding for a class, the parameterized constructor must be annotated with javadoc:org.springframework.beans.factory.annotation.Autowired[format=annotation] or made `private`.
826+
Kotlin developers can use an empty primary constructor to opt-out of constructor binding.
827+
828+
For example:
829+
830+
include-code::primaryconstructor/MyProperties[]
831+
825832
Constructor binding can be used with records.
826833
Unless your record has multiple constructors, there is no need to use javadoc:org.springframework.boot.context.properties.bind.ConstructorBinding[format=annotation].
827834

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.features.externalconfig.typesafeconfigurationproperties.constructorbinding.primaryconstructor;
18+
19+
class MyBean {
20+
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.features.externalconfig.typesafeconfigurationproperties.constructorbinding.primaryconstructor;
18+
19+
import org.springframework.beans.factory.annotation.Autowired;
20+
import org.springframework.boot.context.properties.ConfigurationProperties;
21+
22+
@ConfigurationProperties("my")
23+
public class MyProperties {
24+
25+
// @fold:on // fields...
26+
final MyBean myBean;
27+
28+
private String name;
29+
30+
// @fold:off
31+
32+
@Autowired
33+
public MyProperties(MyBean myBean) {
34+
this.myBean = myBean;
35+
}
36+
37+
// @fold:on // getters / setters...
38+
39+
public String getName() {
40+
return this.name;
41+
}
42+
43+
public void setName(String name) {
44+
this.name = name;
45+
}
46+
47+
// @fold:off
48+
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.features.externalconfig.typesafeconfigurationproperties.constructorbinding.primaryconstructor
18+
19+
import org.springframework.boot.context.properties.ConfigurationProperties
20+
import org.springframework.boot.context.properties.bind.DefaultValue
21+
import java.net.InetAddress
22+
23+
@ConfigurationProperties("my")
24+
class MyProperties() {
25+
26+
constructor(name: String) : this() {
27+
this.name = name
28+
}
29+
30+
// @fold:on // vars...
31+
var name: String? = null
32+
// @fold:off
33+
34+
}

spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/context/properties/bind/KotlinDefaultBindConstructorProviderTests.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,18 @@ class KotlinDefaultBindConstructorProviderTests {
6161
assertThat(bindConstructor).isNull()
6262
}
6363

64+
@Test
65+
fun `type with no param primary constructor and secondary params constructor should not use constructor binding`() {
66+
val bindConstructor = this.constructorProvider.getBindConstructor(NoParamPrimaryWithParamsSecondaryProperties::class.java, false)
67+
assertThat(bindConstructor).isNull()
68+
}
69+
70+
@Test
71+
fun `type with params primary constructor and no param secondary constructor should use constructor binding`() {
72+
val bindConstructor = this.constructorProvider.getBindConstructor(ParamsPrimaryWithNoParamSecondaryProperties::class.java, false)
73+
assertThat(bindConstructor).isNotNull()
74+
}
75+
6476
@Test
6577
fun `type with autowired secondary constructor should not use constructor binding`() {
6678
val bindConstructor = this.constructorProvider.getBindConstructor(AutowiredSecondaryProperties::class.java, false)
@@ -127,6 +139,18 @@ class KotlinDefaultBindConstructorProviderTests {
127139
constructor(@Suppress("UNUSED_PARAMETER") foo: String) : this(foo, 21)
128140
}
129141

142+
class NoParamPrimaryWithParamsSecondaryProperties() {
143+
144+
constructor(@Suppress("UNUSED_PARAMETER") name: String) : this()
145+
146+
var name: String? = null
147+
}
148+
149+
class ParamsPrimaryWithNoParamSecondaryProperties(var name: String?) {
150+
151+
constructor() : this(null)
152+
}
153+
130154
class AutowiredSecondaryProperties {
131155

132156
@Autowired

0 commit comments

Comments
 (0)