diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanInstanceSupplier.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanInstanceSupplier.java index 56d3e79268c7..a1992cb88760 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanInstanceSupplier.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanInstanceSupplier.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -213,12 +213,13 @@ private T invokeBeanSupplier(Executable executable, ThrowingSupplier beanSupp if (!(executable instanceof Method method)) { return beanSupplier.get(); } + Method priorInvokedFactoryMethod = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod(); try { SimpleInstantiationStrategy.setCurrentlyInvokedFactoryMethod(method); return beanSupplier.get(); } finally { - SimpleInstantiationStrategy.setCurrentlyInvokedFactoryMethod(null); + SimpleInstantiationStrategy.setCurrentlyInvokedFactoryMethod(priorInvokedFactoryMethod); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java index d1d98d35e5ff..49c38d7e7389 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -55,12 +55,18 @@ public static Method getCurrentlyInvokedFactoryMethod() { } /** - * Set the factory method currently being invoked or {@code null} to reset. + * Set the factory method currently being invoked or {@code null} to remove + * the current value, if any. * @param method the factory method currently being invoked or {@code null} * @since 6.0 */ public static void setCurrentlyInvokedFactoryMethod(@Nullable Method method) { - currentlyInvokedFactoryMethod.set(method); + if (method != null) { + currentlyInvokedFactoryMethod.set(method); + } + else { + currentlyInvokedFactoryMethod.remove(); + } } @@ -134,9 +140,9 @@ public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, Bean try { ReflectionUtils.makeAccessible(factoryMethod); - Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get(); + Method priorInvokedFactoryMethod = getCurrentlyInvokedFactoryMethod(); try { - currentlyInvokedFactoryMethod.set(factoryMethod); + setCurrentlyInvokedFactoryMethod(factoryMethod); Object result = factoryMethod.invoke(factoryBean, args); if (result == null) { result = new NullBean(); @@ -144,12 +150,7 @@ public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, Bean return result; } finally { - if (priorInvokedFactoryMethod != null) { - currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod); - } - else { - currentlyInvokedFactoryMethod.remove(); - } + setCurrentlyInvokedFactoryMethod(priorInvokedFactoryMethod); } } catch (IllegalArgumentException ex) { diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanInstanceSupplierTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanInstanceSupplierTests.java index 6d8da2605460..1a1a8f6d56fe 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanInstanceSupplierTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanInstanceSupplierTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.stream.Stream; @@ -51,6 +52,7 @@ import org.springframework.beans.factory.support.InstanceSupplier; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.factory.support.SimpleInstantiationStrategy; import org.springframework.core.env.Environment; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.ResourceLoader; @@ -292,6 +294,33 @@ void getNestedWithNoGeneratorUsesReflection(Source source) throws Exception { assertThat(instance).isEqualTo("1"); } + @Test // gh-33180 + void getWithNestedInvocationRetainsFactoryMethod() throws Exception { + AtomicReference testMethodReference = new AtomicReference<>(); + AtomicReference anotherMethodReference = new AtomicReference<>(); + + BeanInstanceSupplier nestedInstanceSupplier = BeanInstanceSupplier + .forFactoryMethod(AnotherTestStringFactory.class, "another") + .withGenerator(registeredBean -> { + anotherMethodReference.set(SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod()); + return "Another"; + }); + RegisteredBean nestedRegisteredBean = new Source(String.class, nestedInstanceSupplier).registerBean(this.beanFactory); + BeanInstanceSupplier instanceSupplier = BeanInstanceSupplier + .forFactoryMethod(TestStringFactory.class, "test") + .withGenerator(registeredBean -> { + Object nested = nestedInstanceSupplier.get(nestedRegisteredBean); + testMethodReference.set(SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod()); + return "custom" + nested; + }); + RegisteredBean registeredBean = new Source(String.class, instanceSupplier).registerBean(this.beanFactory); + Object value = instanceSupplier.get(registeredBean); + + assertThat(value).isEqualTo("customAnother"); + assertThat(testMethodReference.get()).isEqualTo(instanceSupplier.getFactoryMethod()); + assertThat(anotherMethodReference.get()).isEqualTo(nestedInstanceSupplier.getFactoryMethod()); + } + @Test void resolveArgumentsWithNoArgConstructor() { RootBeanDefinition beanDefinition = new RootBeanDefinition( @@ -1030,4 +1059,18 @@ static class MethodOnInterfaceImpl implements MethodOnInterface { } + static class TestStringFactory { + + String test() { + return "test"; + } + } + + static class AnotherTestStringFactory { + + String another() { + return "another"; + } + } + }