spring-boot icon indicating copy to clipboard operation
spring-boot copied to clipboard

Failed to bind properties in 3.0.2 to Kotlin data class during test as no setter found

Open orrc opened this issue 2 years ago • 2 comments

I'm not exactly sure where the problem lies, but we have a test case that was running in Spring Boot 3.0.0, and fails with 3.0.2.

GitHub repo with a tiny reproduction: https://github.com/orrc/spring-boot-prop-binding-repro


With a @ConfigurationProperties class:

@ConfigurationProperties("app.my-config")
data class SomeConfig(
    val someProperty: Int,
)

And values in application.yml:

app:
  myConfig:
    someProperty: 1

Running this test, with an overridden config, fails to start:

@TestConfiguration
class SomeTestConfig {
    @Bean
    fun someConfig() = SomeConfig(
        someProperty = 2,
    )
}

@WebMvcTest(Controller::class)
@Import(SomeTestConfig::class)
class ControllerTest {
    @Test
    fun doNothing() {
        // Fails to start on Spring Boot 3.0.2
    }
}

Logs:

***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to bind properties under 'app.my-config' to com.example.demo.SomeConfig:

    Property: app.my-config.some-property
    Value: "1"
    Origin: class path resource [application.yml] - 3:19
    Reason: java.lang.IllegalStateException: No setter found for property: some-property


2023-01-24T22:44:22.453+01:00 ERROR 71630 --- [    Test worker] o.s.test.context.TestContextManager      : Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener] to prepare test instance [com.example.demo.ControllerTest@2e7af36e]

java.lang.IllegalStateException: Failed to load ApplicationContext for [WebMergedContextConfiguration@4a058df8 testClass = com.example.demo.ControllerTest, locations = [], classes = [com.example.demo.DemoApplication], contextInitializerClasses = [], activeProfiles = [], propertySourceLocations = [], propertySourceProperties = ["org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@6138e79a, org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@9da1, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@7e546387, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@4c20d68, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@5f0e9815, [ImportsContextCustomizer@4b56b031 key = [com.example.demo.SomeTestConfig, org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration, org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration, org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration, org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration, org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration, org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration, org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration, org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration, org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration, org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration, org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebClientAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebDriverAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityConfiguration, org.springframework.boot.test.autoconfigure.web.reactive.WebTestClientAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@37091312, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@550a1967, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.context.SpringBootTestAnnotation@19aa2405], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:142) ~[spring-test-6.0.4.jar:6.0.4]
	at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:127) ~[spring-test-6.0.4.jar:6.0.4]
	at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:141) ~[spring-test-6.0.4.jar:6.0.4]
	at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:97) ~[spring-test-6.0.4.jar:6.0.4]
	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:241) ~[spring-test-6.0.4.jar:6.0.4]
	at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:138) ~[spring-test-6.0.4.jar:6.0.4]
  …
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'controller' defined in file [/Users/chris/code/demo/build/classes/kotlin/main/com/example/demo/Controller.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'someConfig': Could not bind properties to 'SomeConfig' : prefix=app.my-config, ignoreInvalidFields=false, ignoreUnknownFields=true
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:798) ~[spring-beans-6.0.4.jar:6.0.4]
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:245) ~[spring-beans-6.0.4.jar:6.0.4]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1344) ~[spring-beans-6.0.4.jar:6.0.4]
  …
Caused by: org.springframework.boot.context.properties.ConfigurationPropertiesBindException: Error creating bean with name 'someConfig': Could not bind properties to 'SomeConfig' : prefix=app.my-config, ignoreInvalidFields=false, ignoreUnknownFields=true
	at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.bind(ConfigurationPropertiesBindingPostProcessor.java:99) ~[spring-boot-3.0.2.jar:3.0.2]
	at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:79) ~[spring-boot-3.0.2.jar:3.0.2]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:420) ~[spring-beans-6.0.4.jar:6.0.4]
  …
Caused by: org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'app.my-config' to com.example.demo.SomeConfig
	at org.springframework.boot.context.properties.bind.Binder.handleBindError(Binder.java:387) ~[spring-boot-3.0.2.jar:3.0.2]
	at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:347) ~[spring-boot-3.0.2.jar:3.0.2]
	at org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$4(Binder.java:472) ~[spring-boot-3.0.2.jar:3.0.2]
	at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:98) ~[spring-boot-3.0.2.jar:3.0.2]
  …
Caused by: java.lang.IllegalStateException: No setter found for property: some-property
	at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:107) ~[spring-boot-3.0.2.jar:3.0.2]
	at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:86) ~[spring-boot-3.0.2.jar:3.0.2]
	at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:62) ~[spring-boot-3.0.2.jar:3.0.2]
	at org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$5(Binder.java:476) ~[spring-boot-3.0.2.jar:3.0.2]
	at org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:590) ~[spring-boot-3.0.2.jar:3.0.2]
	at org.springframework.boot.context.properties.bind.Binder$Context.withDataObject(Binder.java:576) ~[spring-boot-3.0.2.jar:3.0.2]
	at org.springframework.boot.context.properties.bind.Binder.bindDataObject(Binder.java:474) ~[spring-boot-3.0.2.jar:3.0.2]
	at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:414) ~[spring-boot-3.0.2.jar:3.0.2]
	at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:343) ~[spring-boot-3.0.2.jar:3.0.2]
	... 140 common frames omitted

orrc avatar Jan 24 '23 21:01 orrc

Annotating the config class constructor with @ConstructorBinding appears to make things work as they did in 3.0.0:

--- a/src/main/kotlin/com/example/demo/SomeConfig.kt
+++ b/src/main/kotlin/com/example/demo/SomeConfig.kt
@@ -3,5 +3,6 @@ package com.example.demo
 import org.springframework.boot.context.properties.ConfigurationProperties
+import org.springframework.boot.context.properties.bind.ConstructorBinding

 @ConfigurationProperties("app.my-config")
-data class SomeConfig(
+data class SomeConfig @ConstructorBinding constructor(
     val someProperty: Int,

orrc avatar Jan 24 '23 22:01 orrc

Could be related to https://github.com/spring-projects/spring-boot/issues/33849

mhalbritter avatar Jan 25 '23 08:01 mhalbritter

It might also be related to #33710

philwebb avatar Feb 03 '23 03:02 philwebb

I don't think its working with 3.0.0 for me. I cannot override the default settings for the following in application.yml:

management:
  metrics:
    export:
      statsd:
        host: ${STATSD_HOST}
        enabled: true

The STATSD_HOST environment variable is never loaded as a property override. I tried using Java properties on the command line too and that didn't work.

Only the default settings are used. This works fine for me while using 2.x.

travispeloton avatar Feb 03 '23 21:02 travispeloton

@travispeloton Did you mean to comment on this issue? I'm not sure I see how your problem is related to Kotlin data classes.

philwebb avatar Feb 04 '23 00:02 philwebb

This issue appeared relevant. I'm using Kotlin as well, and Spring 3.x wont allow me to configure the properties I mentioned in the comment above. It always uses the default values. There is something bigger broken here related to property binding.

travispeloton avatar Feb 05 '23 14:02 travispeloton

@travispeloton This issue is specifically a regression in Spring Boot 3.0.2.

We are able to bind config properties and override them with environment variables at runtime in 3.0.x, so you should probably open a new issue with more details, including a minimal reproduction case, separate from statsd configuration.

orrc avatar Feb 05 '23 15:02 orrc

After updating to Spring Boot 3.0.2, we were able to continue with the workaround above, and things worked fine. Now that we're trying to update to Spring Boot 3.1.2, neither our original code nor the workaround works anymore.

This feels potentially similar to #36175, though the symptoms they were seeing seem to be different. So I'm still a bit lost about exactly what the problem is, or whether we're just doing something completely wrong… 🤔


It should be possible reproduce with the GitHub repository mentioned above:

git clone https://github.com/orrc/spring-boot-prop-binding-repro
cd spring-boot-prop-binding-repro

✅ Succeeds with Spring Boot 3.0.1

Downgrade to 3.0.1 and run the tests:

sed -i 's/3.0.2/3.0.1/' build.gradle.kts

./gradlew test
# BUILD SUCCESSFUL

:x: Fails with Spring Boot 3.0.2

Reset to 3.0.2 and run the tests:

git checkout -- .

./gradlew test
# BUILD FAILED

✅ Succeeds with Spring Boot 3.0.2 + workaround

Apply the workaround to annotate the constructor with @ConstructorBinding, and run the tests:

patch -p1 << EOF
--- a/src/main/kotlin/com/example/demo/SomeConfig.kt
+++ b/src/main/kotlin/com/example/demo/SomeConfig.kt
@@ -3,0 +4 @@ import org.springframework.boot.context.properties.ConfigurationProperties
+import org.springframework.boot.context.properties.bind.ConstructorBinding
@@ -6 +7 @@ import org.springframework.boot.context.properties.ConfigurationProperties
-data class SomeConfig(
+data class SomeConfig @ConstructorBinding constructor(
EOF

./gradlew test
# BUILD SUCCESSFUL

✅ Succeeds with Spring Boot 3.1.0 + workaround

Upgrade to 3.1.0 and run the tests:

sed -i 's/3.0.2/3.1.0/' build.gradle.kts

./gradlew test
# BUILD SUCCESSFUL

:x: Fails with Spring Boot 3.1.1 + workaround

Upgrade to 3.1.1 and run the tests:

sed -i 's/3.1.0/3.1.1/' build.gradle.kts

./gradlew test
# BUILD FAILED

:x: Fails with Spring Boot 3.1.1, without workaround

Remove the workaround, and it still fails:

git checkout -- src/

./gradlew test
# BUILD FAILED

orrc avatar Jul 25 '23 15:07 orrc

This isn't specific to Kotlin. The same problem occurs with a Java class like this:

@ConfigurationProperties("app.myconfig")
public class SomeConfig {
	
	private final int someProperty;
	
	public SomeConfig(int someProperty) {
		this.someProperty = someProperty;
	}
	
	public int getSomeProperty() {
		return this.someProperty;
	}

}

Using @ConstructorBinding to prevent the attempt at JavaBean-based binding works up until 3.0.7 inclusive. I haven't tried every version, but it would appear that it never worked in 2.7.x. It either fails with the "no setter found" error or, when adding @ConstructorBinding, it fails because it has been added to a regular bean.

wilkinsona avatar Jul 25 '23 19:07 wilkinsona

So I ran into this when trying to updated a project to 3.0.10 from 3.0.7. It appears to occur because spring instantiates the bean using the factory method (in the example repo, by calling SomeTestConfig.someConfig(), and then tries to modify the properties of the bean from the application.yml. Since the properties are indeed immutable, the attempt to modify them from the YAML file fails. That's probably as it should be, if it's meant to be immutable. I'm not sure how it worked prior to 3.0.8, but I assume that that spring was just not trying to apply the YAML. Maybe it should be fixed to revert to that behavior? At the least, a more helpful error message might be useful.

rossLodge-R avatar Aug 28 '23 17:08 rossLodge-R

I see the same issue with Kotlin data class when upgrading to spring-boot 3.1.5 from 2.7.12. I removed the @ConstructorBinding annotation as suggested in the 3.0 migration guide and started getting the error - Failed to bind properties under....

Can someone please confirm if this is being looked at?

Vignesh-Au avatar Oct 25 '23 19:10 Vignesh-Au

@Vignesh-Au it sounds like you have a different problem. As I said above, I don't think the situation that this issue is tracking ever worked with 2.7.x. I believe it only worked from 3.0.0 to 3.0.7 inclusive. Please share a minimal example that works with 2.7.12 and fails with 3.1.5.

wilkinsona avatar Oct 26 '23 07:10 wilkinsona

package com.example.gh_33969;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;

import com.example.gh_33969.Gh33969ApplicationTests.SomeConfiguration;

@SpringBootTest(classes = SomeConfiguration.class, properties = "immutable.property=some-value")
class Gh33969ApplicationTests {
	
	@Autowired
	private ImmutableProperties properties;

	@Test
	void contextLoads() {
		System.out.println(properties.getProperty());
	}
	
	@SpringBootConfiguration
	@EnableConfigurationProperties
	static class SomeConfiguration {
		
		@Bean
		ImmutableProperties immutableProperties() {
			return new ImmutableProperties("value");
		}
		
	}

}


@ConfigurationProperties("immutable")
class ImmutableProperties {
	
	private final String property;

	public ImmutableProperties(String property) {
		this.property = property;
	}

	public String getProperty() {
		return property;
	}

}

The problem does not occur if ImmutableProperties is a record rather than a class.

wilkinsona avatar Jun 04 '24 17:06 wilkinsona

I ran into this with records too, I posted it as question on StackOverflow: https://stackoverflow.com/questions/78586486/spring-boot-3-3-0-configurationproperties-not-working-with-bean I thought it was something I did wrong, but could it be this issue?

It seems it cannot do constructor binding on the properties record. Nested records are not a problem. Success: class with no-arg-const+setters & record params Failure: record with record params Failure: class with RequiredArgConstructors (final params) & record params

barbetb avatar Jun 06 '24 12:06 barbetb

The problem does not occur if ImmutableProperties is a record rather than a class.

This is only the case when the record does not resemble a JavaBean. If it has JavaBean-style getters, the problem occurs:

@ConfigurationProperties(prefix = "immutable")
record ImmutableProperties(String property) {

	String getProperty() {
		return this.property;
	}

}

We can avoid the binding attempt by deducing the bind method and not binding when it's VALUE_OBJECT. This works for the scenarios described in this issue. It does not work for the situation described in #33710 where the object can be created using constructor binding but it also has some JavaBean-style properties that need to be bound.

There are two different situations here:

  1. The instance returned from the @Bean method is fully initialized and no binding should be performed
  2. The instance returned from the @Bean method is only a starting point and binding should be performed

I don't think it's possible for us to infer the first situation with 100% accuracy. We may be able to make things more accurate than they are today by skipping binding if we can detect that the bean's immutable, but that would not help in situations where the bean could be mutated but we don't want it to be.

The situation where the bean could be mutated but we don't want it to be arguably applies more broadly. In the situation in this issue's opening comment, if SomeConfig was mutable, some-property would be set to 1 by configuration property binding when the intent was for its value to be 2. It feels like we need a way for configuration property binding to be disabled for an instance of a @ConfigurationProperties-annotated class that's returned from a @Bean method.

wilkinsona avatar Jun 11 '24 08:06 wilkinsona

We discussed this today and we're considering adding a bindMode attribute to @ConfigurationProperties with values of DEFAULT, JAVA_BEAN, VALUE_OBJECT (names can be tweaked). If the attribute is set we'll take that as a signal of the type of binding wanted.

We think this might also allow us to get rid of the @Autowired on constructor requirement that we currently have when users don't want constructor binding.

We can also look into supporting this on the bean method as way of overriding the version declared on the class.

philwebb avatar Jun 12 '24 14:06 philwebb

In the meantime, we would recommend not creating a bean for this purpose but instead use a @PropertySource on the test and rely on the real binding.

philwebb avatar Jun 12 '24 14:06 philwebb

After some discussion we're going to repurpose this issue to ensure that immutable @ConfigurationProperties can be used with the upcoming Spring Framework @BeanOverride annotation.

In the meantime, if using a @PropertySource isn't an option, you can instead add a BindMethod attribute to the bean definition with a BindMethod.VALUE_OBJECT value which will prevent the bean based binding from happening.

Here's an updated example that does that using a ApplicationContextInitializer:

package com.example.gh_33969;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.bind.BindMethod;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.ContextConfiguration;

import com.example.gh_33969.Gh33969ApplicationTests.OverrideConfigurationProperties;
import com.example.gh_33969.Gh33969ApplicationTests.SomeConfiguration;

@SpringBootTest(classes = SomeConfiguration.class, properties = "immutable.property=some-value")
@ContextConfiguration(initializers = OverrideConfigurationProperties.class)
class Gh33969ApplicationTests {

	@Autowired
	private ImmutableProperties properties;

	@Test
	void contextLoads() {
		System.out.println(properties.getProperty());
	}

	@SpringBootConfiguration
	@EnableConfigurationProperties
	static class SomeConfiguration {

	}

	static class OverrideConfigurationProperties implements ApplicationContextInitializer<GenericApplicationContext> {

		@Override
		public void initialize(GenericApplicationContext applicationContext) {
			applicationContext.registerBean(ImmutableProperties.class,
					() -> new ImmutableProperties("value"),
					this::disableBeanBinding);
		}

		private void disableBeanBinding(BeanDefinition beanDefinition) {
			beanDefinition.setAttribute(BindMethod.class.getName(), BindMethod.VALUE_OBJECT);
		}

	}

}

@ConfigurationProperties("immutable")
class ImmutableProperties {

	private final String property;

	public ImmutableProperties(String property) {
		this.property = property;
	}

	public String getProperty() {
		return property;
	}

}

philwebb avatar Jun 18 '24 17:06 philwebb

This works as we'd hoped with Spring Framework 6.2's @TestBean:

package com.example.gh_33969;

import org.junit.jupiter.api.Test;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.bean.override.convention.TestBean;

@SpringBootTest(properties = "immutable.property=some-value")
class Gh33969ApplicationTests {

	@TestBean
	private ImmutableProperties properties;

	@Test
	void contextLoads() {
		System.out.println(this.properties.getProperty());
	}

	static ImmutableProperties properties() {
		return new ImmutableProperties("test-bean");
	}

	@Configuration(proxyBeanMethods = false)
	@EnableConfigurationProperties(ImmutableProperties.class)
	static class SomeConfiguration {

		SomeConfiguration(ImmutableProperties properties) {
			System.out.println(properties.getProperty());
		}

	}

}

@ConfigurationProperties("immutable")
class ImmutableProperties {

	private final String property;

	public ImmutableProperties(String property) {
		this.property = property;
	}

	public String getProperty() {
		return this.property;
	}

}

When run, the above will output test-bean twice. If @TestBean is replaced with @Autowired, it will output some-value twice. I've just pushed a test that verifies this behavior.

wilkinsona avatar Jun 24 '24 18:06 wilkinsona