Allow @ConstructorBinding to be optional
This commit makes @ConstructorBinding optional for a type that has a single parameterized constructor. An @Autowired annotation on any of the constructors indicates that the type should not be constructor bound. Since @ConstructorBinding is now deduced for a single parameterized constructor, the annotation is no longer needed at the type level. Closes gh-23216
This commit is contained in:
@@ -410,21 +410,6 @@ class ConfigurationMetadataAnnotationProcessorTests extends AbstractMetadataGene
|
||||
compile(RecursiveProperties.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledForJreRange(min = JRE.JAVA_16)
|
||||
void explicityBoundRecordProperties(@TempDir File temp) throws IOException {
|
||||
File exampleRecord = new File(temp, "ExampleRecord.java");
|
||||
try (PrintWriter writer = new PrintWriter(new FileWriter(exampleRecord))) {
|
||||
writer.println("@org.springframework.boot.configurationsample.ConstructorBinding");
|
||||
writer.println("@org.springframework.boot.configurationsample.ConfigurationProperties(\"explicit\")");
|
||||
writer.println("public record ExampleRecord(String someString, Integer someInteger) {");
|
||||
writer.println("}");
|
||||
}
|
||||
ConfigurationMetadata metadata = compile(exampleRecord);
|
||||
assertThat(metadata).has(Metadata.withProperty("explicit.some-string"));
|
||||
assertThat(metadata).has(Metadata.withProperty("explicit.some-integer"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledForJreRange(min = JRE.JAVA_16)
|
||||
void implicitlyBoundRecordProperties(@TempDir File temp) throws IOException {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
@@ -45,6 +45,7 @@ class MetadataGenerationEnvironmentFactory implements Function<ProcessingEnviron
|
||||
TestConfigurationMetadataAnnotationProcessor.NESTED_CONFIGURATION_PROPERTY_ANNOTATION,
|
||||
TestConfigurationMetadataAnnotationProcessor.DEPRECATED_CONFIGURATION_PROPERTY_ANNOTATION,
|
||||
TestConfigurationMetadataAnnotationProcessor.CONSTRUCTOR_BINDING_ANNOTATION,
|
||||
TestConfigurationMetadataAnnotationProcessor.AUTOWIRED_ANNOTATION,
|
||||
TestConfigurationMetadataAnnotationProcessor.DEFAULT_VALUE_ANNOTATION, endpointAnnotations,
|
||||
TestConfigurationMetadataAnnotationProcessor.READ_OPERATION_ANNOTATION,
|
||||
TestConfigurationMetadataAnnotationProcessor.NAME_ANNOTATION);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
@@ -35,6 +35,7 @@ import org.springframework.boot.configurationprocessor.metadata.ItemMetadata;
|
||||
import org.springframework.boot.configurationprocessor.test.RoundEnvironmentTester;
|
||||
import org.springframework.boot.configurationprocessor.test.TestableAnnotationProcessor;
|
||||
import org.springframework.boot.configurationsample.immutable.ImmutableClassConstructorBindingProperties;
|
||||
import org.springframework.boot.configurationsample.immutable.ImmutableDeducedConstructorBindingProperties;
|
||||
import org.springframework.boot.configurationsample.immutable.ImmutableMultiConstructorProperties;
|
||||
import org.springframework.boot.configurationsample.immutable.ImmutableNameAnnotationProperties;
|
||||
import org.springframework.boot.configurationsample.immutable.ImmutableSimpleProperties;
|
||||
@@ -42,12 +43,11 @@ import org.springframework.boot.configurationsample.lombok.LombokExplicitPropert
|
||||
import org.springframework.boot.configurationsample.lombok.LombokSimpleDataProperties;
|
||||
import org.springframework.boot.configurationsample.lombok.LombokSimpleProperties;
|
||||
import org.springframework.boot.configurationsample.lombok.LombokSimpleValueProperties;
|
||||
import org.springframework.boot.configurationsample.simple.AutowiredProperties;
|
||||
import org.springframework.boot.configurationsample.simple.HierarchicalProperties;
|
||||
import org.springframework.boot.configurationsample.simple.HierarchicalPropertiesGrandparent;
|
||||
import org.springframework.boot.configurationsample.simple.HierarchicalPropertiesParent;
|
||||
import org.springframework.boot.configurationsample.simple.SimpleProperties;
|
||||
import org.springframework.boot.configurationsample.specific.MatchingConstructorNoDirectiveProperties;
|
||||
import org.springframework.boot.configurationsample.specific.TwoConstructorsClassConstructorBindingExample;
|
||||
import org.springframework.boot.configurationsample.specific.TwoConstructorsExample;
|
||||
import org.springframework.boot.testsupport.compiler.TestCompiler;
|
||||
|
||||
@@ -111,6 +111,14 @@ class PropertyDescriptorResolverTests {
|
||||
(stream) -> assertThat(stream).containsExactly("name", "description", "counter", "number", "items")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void propertiesWithDeducedConstructorBinding() throws IOException {
|
||||
process(ImmutableDeducedConstructorBindingProperties.class,
|
||||
propertyNames((stream) -> assertThat(stream).containsExactly("theName", "flag")));
|
||||
process(ImmutableDeducedConstructorBindingProperties.class, properties((stream) -> assertThat(stream)
|
||||
.allMatch((predicate) -> predicate instanceof ConstructorParameterPropertyDescriptor)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void propertiesWithConstructorWithConstructorBinding() throws IOException {
|
||||
process(ImmutableSimpleProperties.class, propertyNames(
|
||||
@@ -128,16 +136,9 @@ class PropertyDescriptorResolverTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void propertiesWithConstructorAndClassConstructorBindingAndSeveralCandidates() throws IOException {
|
||||
process(TwoConstructorsClassConstructorBindingExample.class,
|
||||
propertyNames((stream) -> assertThat(stream).isEmpty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void propertiesWithConstructorNoDirective() throws IOException {
|
||||
process(MatchingConstructorNoDirectiveProperties.class,
|
||||
propertyNames((stream) -> assertThat(stream).containsExactly("name")));
|
||||
process(MatchingConstructorNoDirectiveProperties.class, properties((stream) -> assertThat(stream)
|
||||
void propertiesWithAutowiredConstructor() throws IOException {
|
||||
process(AutowiredProperties.class, propertyNames((stream) -> assertThat(stream).containsExactly("theName")));
|
||||
process(AutowiredProperties.class, properties((stream) -> assertThat(stream)
|
||||
.allMatch((predicate) -> predicate instanceof JavaBeanPropertyDescriptor)));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
@@ -59,6 +59,8 @@ public class TestConfigurationMetadataAnnotationProcessor extends ConfigurationM
|
||||
|
||||
public static final String CONSTRUCTOR_BINDING_ANNOTATION = "org.springframework.boot.configurationsample.ConstructorBinding";
|
||||
|
||||
public static final String AUTOWIRED_ANNOTATION = "org.springframework.boot.configurationsample.Autowired";
|
||||
|
||||
public static final String DEFAULT_VALUE_ANNOTATION = "org.springframework.boot.configurationsample.DefaultValue";
|
||||
|
||||
public static final String CONTROLLER_ENDPOINT_ANNOTATION = "org.springframework.boot.configurationsample.ControllerEndpoint";
|
||||
@@ -105,6 +107,11 @@ public class TestConfigurationMetadataAnnotationProcessor extends ConfigurationM
|
||||
return CONSTRUCTOR_BINDING_ANNOTATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String autowiredAnnotation() {
|
||||
return AUTOWIRED_ANNOTATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String defaultValueAnnotation() {
|
||||
return DEFAULT_VALUE_ANNOTATION;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
@@ -22,10 +22,15 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
/**
|
||||
* Alternative to Spring Framework's {@code @Autowired} for testing (removes the need for
|
||||
* a dependency on the real annotation).
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.CONSTRUCTOR })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@ConstructorBinding
|
||||
public @interface MetaConstructorBinding {
|
||||
public @interface Autowired {
|
||||
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import java.lang.annotation.Target;
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.CONSTRUCTOR })
|
||||
@Target(ElementType.CONSTRUCTOR)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface ConstructorBinding {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.boot.configurationsample.immutable;
|
||||
|
||||
import org.springframework.boot.configurationsample.ConfigurationProperties;
|
||||
import org.springframework.boot.configurationsample.ConstructorBinding;
|
||||
import org.springframework.boot.configurationsample.DefaultValue;
|
||||
|
||||
/**
|
||||
@@ -26,7 +25,6 @@ import org.springframework.boot.configurationsample.DefaultValue;
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@ConfigurationProperties("test")
|
||||
@ConstructorBinding
|
||||
public class DeducedImmutableClassProperties {
|
||||
|
||||
private final Nested nested;
|
||||
|
||||
@@ -16,15 +16,12 @@
|
||||
|
||||
package org.springframework.boot.configurationsample.immutable;
|
||||
|
||||
import org.springframework.boot.configurationsample.MetaConstructorBinding;
|
||||
|
||||
/**
|
||||
* Simple immutable properties with several constructors.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@MetaConstructorBinding
|
||||
public class ImmutableClassConstructorBindingProperties {
|
||||
|
||||
private final String name;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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
|
||||
*
|
||||
* https://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.immutable;
|
||||
|
||||
import org.springframework.boot.configurationsample.ConfigurationProperties;
|
||||
import org.springframework.boot.configurationsample.DefaultValue;
|
||||
|
||||
/**
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@ConfigurationProperties("immutable")
|
||||
public class ImmutableDeducedConstructorBindingProperties {
|
||||
|
||||
/**
|
||||
* The name of these properties.
|
||||
*/
|
||||
private final String theName;
|
||||
|
||||
/**
|
||||
* A simple flag.
|
||||
*/
|
||||
private final boolean flag;
|
||||
|
||||
public ImmutableDeducedConstructorBindingProperties(@DefaultValue("boot") String theName, boolean flag) {
|
||||
this.theName = theName;
|
||||
this.flag = flag;
|
||||
}
|
||||
|
||||
public String getTheName() {
|
||||
return this.theName;
|
||||
}
|
||||
|
||||
public boolean isFlag() {
|
||||
return this.flag;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.boot.configurationsample.immutable;
|
||||
|
||||
import org.springframework.boot.configurationsample.ConfigurationProperties;
|
||||
import org.springframework.boot.configurationsample.ConstructorBinding;
|
||||
import org.springframework.boot.configurationsample.Name;
|
||||
|
||||
/**
|
||||
@@ -26,7 +25,6 @@ import org.springframework.boot.configurationsample.Name;
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@ConfigurationProperties("named")
|
||||
@ConstructorBinding
|
||||
public class ImmutableNameAnnotationProperties {
|
||||
|
||||
private final String imports;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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
|
||||
*
|
||||
* https://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.simple;
|
||||
|
||||
import org.springframework.boot.configurationsample.Autowired;
|
||||
|
||||
/**
|
||||
* Properties with autowired constructor.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
public class AutowiredProperties {
|
||||
|
||||
/**
|
||||
* The name of this simple properties.
|
||||
*/
|
||||
private String theName;
|
||||
|
||||
@Autowired
|
||||
public AutowiredProperties(String theName) {
|
||||
this.theName = theName;
|
||||
}
|
||||
|
||||
public String getTheName() {
|
||||
return this.theName;
|
||||
}
|
||||
|
||||
public void setTheName(String name) {
|
||||
this.theName = name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.boot.configurationsample.specific;
|
||||
|
||||
import org.springframework.boot.configurationsample.ConfigurationProperties;
|
||||
import org.springframework.boot.configurationsample.ConstructorBinding;
|
||||
import org.springframework.boot.configurationsample.DefaultValue;
|
||||
|
||||
/**
|
||||
@@ -27,7 +26,6 @@ import org.springframework.boot.configurationsample.DefaultValue;
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@ConfigurationProperties("test")
|
||||
@ConstructorBinding
|
||||
public class InvalidDefaultValueFloatingPointProperties {
|
||||
|
||||
private final Double ratio;
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.boot.configurationsample.specific;
|
||||
|
||||
import org.springframework.boot.configurationsample.ConfigurationProperties;
|
||||
import org.springframework.boot.configurationsample.DefaultValue;
|
||||
import org.springframework.boot.configurationsample.MetaConstructorBinding;
|
||||
|
||||
/**
|
||||
* Demonstrates that an invalid default number value leads to a compilation failure.
|
||||
@@ -26,7 +25,6 @@ import org.springframework.boot.configurationsample.MetaConstructorBinding;
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@ConfigurationProperties("test")
|
||||
@MetaConstructorBinding
|
||||
public class InvalidDefaultValueNumberProperties {
|
||||
|
||||
private final int counter;
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.MetaConstructorBinding;
|
||||
|
||||
/**
|
||||
* A type that declares constructor binding but with two available constructors.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@MetaConstructorBinding
|
||||
@SuppressWarnings("unused")
|
||||
public class TwoConstructorsClassConstructorBindingExample {
|
||||
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
public TwoConstructorsClassConstructorBindingExample(String name) {
|
||||
this(name, null);
|
||||
}
|
||||
|
||||
public TwoConstructorsClassConstructorBindingExample(String name, String description) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user