Skip to content

Commit 2eeffe7

Browse files
committed
Add @name support for value object binding
Update value object binder support so that parameters can be annotated with `@Name` if a specific property name should be used. Prior to this commit is was not possible to use Java reserved words as property names. Closes gh-22492
1 parent 2d3ac4b commit 2eeffe7

File tree

3 files changed

+74
-1
lines changed

3 files changed

+74
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2012-2020 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.context.properties.bind;
18+
19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
/**
26+
* Annotation that can be used to specify the name when binding to an immutable property.
27+
* This annotation may be required when binding to names that clash with reserved language
28+
* keywords.
29+
*
30+
* @author Phillip Webb
31+
* @since 2.4.0
32+
*/
33+
@Retention(RetentionPolicy.RUNTIME)
34+
@Target({ ElementType.PARAMETER })
35+
@Documented
36+
public @interface Name {
37+
38+
/**
39+
* The name of the property to use for binding.
40+
* @return the property name
41+
*/
42+
String value();
43+
44+
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
import org.springframework.core.MethodParameter;
3737
import org.springframework.core.ParameterNameDiscoverer;
3838
import org.springframework.core.ResolvableType;
39+
import org.springframework.core.annotation.MergedAnnotation;
40+
import org.springframework.core.annotation.MergedAnnotations;
3941
import org.springframework.core.convert.ConversionException;
4042
import org.springframework.util.Assert;
4143

@@ -245,7 +247,8 @@ private static List<ConstructorParameter> parseConstructorParameters(Constructor
245247
Parameter[] parameters = constructor.getParameters();
246248
List<ConstructorParameter> result = new ArrayList<>(parameters.length);
247249
for (int i = 0; i < parameters.length; i++) {
248-
String name = names[i];
250+
String name = MergedAnnotations.from(parameters[i]).get(Name.class)
251+
.getValue(MergedAnnotation.VALUE, String.class).orElse(names[i]);
249252
ResolvableType parameterType = ResolvableType.forMethodParameter(new MethodParameter(constructor, i),
250253
type);
251254
Annotation[] annotations = parameters[i].getDeclaredAnnotations();

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
* Tests for {@link ValueObjectBinder}.
4141
*
4242
* @author Madhura Bhave
43+
* @author Phillip Webb
4344
*/
4445
class ValueObjectBinderTests {
4546

@@ -332,6 +333,17 @@ void bindWhenBindingToPathTypeWithDefaultValue() { // gh-21263
332333
assertThat(bound.getPath()).isEqualTo(Paths.get("default_value"));
333334
}
334335

336+
@Test
337+
void bindToAnnotationNamedParameter() {
338+
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
339+
source.put("test.import", "test");
340+
this.sources.add(source);
341+
Bindable<NamedParameter> target = Bindable.of(NamedParameter.class);
342+
NamedParameter bound = this.binder.bindOrCreate("test", target);
343+
assertThat(bound.getImportName()).isEqualTo("test");
344+
345+
}
346+
335347
private void noConfigurationProperty(BindException ex) {
336348
assertThat(ex.getProperty()).isNull();
337349
}
@@ -730,4 +742,18 @@ Path getPath() {
730742

731743
}
732744

745+
static class NamedParameter {
746+
747+
private final String importName;
748+
749+
NamedParameter(@Name("import") String importName) {
750+
this.importName = importName;
751+
}
752+
753+
String getImportName() {
754+
return this.importName;
755+
}
756+
757+
}
758+
733759
}

0 commit comments

Comments
 (0)