From 6de624d537065562beb50ac6437ae33e413311c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 10 Jul 2024 13:12:12 +0200 Subject: [PATCH] Provide a template method to expose the currently invoked factory method This commit improves SimpleInstantiationStrategy by providing a common template method before the regular runtime and AOT. As a result, the method to set the currently invoked factory method is deprecated as it should no longer be used. Closes gh-33192 --- .../factory/aot/BeanInstanceSupplier.java | 9 +- .../support/SimpleInstantiationStrategy.java | 82 ++++++++++++------- 2 files changed, 53 insertions(+), 38 deletions(-) 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 39ad1126ed..8f9d2be63c 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 @@ -213,14 +213,7 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impl if (!(executable instanceof Method method)) { return beanSupplier.get(); } - Method priorInvokedFactoryMethod = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod(); - try { - SimpleInstantiationStrategy.setCurrentlyInvokedFactoryMethod(method); - return beanSupplier.get(); - } - finally { - SimpleInstantiationStrategy.setCurrentlyInvokedFactoryMethod(priorInvokedFactoryMethod); - } + return SimpleInstantiationStrategy.instantiateWithFactoryMethod(method, beanSupplier::get); } @Nullable 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 49c38d7e73..f4a59401ae 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 @@ -27,6 +27,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.lang.Nullable; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; +import org.springframework.util.function.ThrowingSupplier; /** * Simple object instantiation strategy for use in a BeanFactory. @@ -59,7 +60,9 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy { * the current value, if any. * @param method the factory method currently being invoked or {@code null} * @since 6.0 + * @deprecated in favor of {@link #instantiateWithFactoryMethod(Method, ThrowingSupplier)} */ + @Deprecated(since = "6.2", forRemoval = true) public static void setCurrentlyInvokedFactoryMethod(@Nullable Method method) { if (method != null) { currentlyInvokedFactoryMethod.set(method); @@ -69,6 +72,31 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy { } } + /** + * Invoke the given {@code instanceSupplier} with the factory method exposed + * as being invoked. + * @param method the factory method to expose + * @param instanceSupplier the instance supplier + * @param the type of the instance + * @return the result of the instance supplier + * @since 6.2 + */ + public static T instantiateWithFactoryMethod(Method method, ThrowingSupplier instanceSupplier) { + Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get(); + try { + currentlyInvokedFactoryMethod.set(method); + return instanceSupplier.get(); + } + finally { + if (priorInvokedFactoryMethod != null) { + currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod); + } + else { + currentlyInvokedFactoryMethod.remove(); + } + } + } + @Override public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) { @@ -137,46 +165,40 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy { public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, @Nullable Object factoryBean, Method factoryMethod, Object... args) { - try { - ReflectionUtils.makeAccessible(factoryMethod); - - Method priorInvokedFactoryMethod = getCurrentlyInvokedFactoryMethod(); + return instantiateWithFactoryMethod(factoryMethod, () -> { try { - setCurrentlyInvokedFactoryMethod(factoryMethod); + ReflectionUtils.makeAccessible(factoryMethod); Object result = factoryMethod.invoke(factoryBean, args); if (result == null) { result = new NullBean(); } return result; } - finally { - setCurrentlyInvokedFactoryMethod(priorInvokedFactoryMethod); - } - } - catch (IllegalArgumentException ex) { - if (factoryBean != null && !factoryMethod.getDeclaringClass().isAssignableFrom(factoryBean.getClass())) { + catch (IllegalArgumentException ex) { + if (factoryBean != null && !factoryMethod.getDeclaringClass().isAssignableFrom(factoryBean.getClass())) { + throw new BeanInstantiationException(factoryMethod, + "Illegal factory instance for factory method '" + factoryMethod.getName() + "'; " + + "instance: " + factoryBean.getClass().getName(), ex); + } throw new BeanInstantiationException(factoryMethod, - "Illegal factory instance for factory method '" + factoryMethod.getName() + "'; " + - "instance: " + factoryBean.getClass().getName(), ex); + "Illegal arguments to factory method '" + factoryMethod.getName() + "'; " + + "args: " + StringUtils.arrayToCommaDelimitedString(args), ex); } - throw new BeanInstantiationException(factoryMethod, - "Illegal arguments to factory method '" + factoryMethod.getName() + "'; " + - "args: " + StringUtils.arrayToCommaDelimitedString(args), ex); - } - catch (IllegalAccessException ex) { - throw new BeanInstantiationException(factoryMethod, - "Cannot access factory method '" + factoryMethod.getName() + "'; is it public?", ex); - } - catch (InvocationTargetException ex) { - String msg = "Factory method '" + factoryMethod.getName() + "' threw exception with message: " + - ex.getTargetException().getMessage(); - if (bd.getFactoryBeanName() != null && owner instanceof ConfigurableBeanFactory cbf && - cbf.isCurrentlyInCreation(bd.getFactoryBeanName())) { - msg = "Circular reference involving containing bean '" + bd.getFactoryBeanName() + "' - consider " + - "declaring the factory method as static for independence from its containing instance. " + msg; + catch (IllegalAccessException ex) { + throw new BeanInstantiationException(factoryMethod, + "Cannot access factory method '" + factoryMethod.getName() + "'; is it public?", ex); } - throw new BeanInstantiationException(factoryMethod, msg, ex.getTargetException()); - } + catch (InvocationTargetException ex) { + String msg = "Factory method '" + factoryMethod.getName() + "' threw exception with message: " + + ex.getTargetException().getMessage(); + if (bd.getFactoryBeanName() != null && owner instanceof ConfigurableBeanFactory cbf && + cbf.isCurrentlyInCreation(bd.getFactoryBeanName())) { + msg = "Circular reference involving containing bean '" + bd.getFactoryBeanName() + "' - consider " + + "declaring the factory method as static for independence from its containing instance. " + msg; + } + throw new BeanInstantiationException(factoryMethod, msg, ex.getTargetException()); + } + }); } }