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
This commit is contained in:
		
							parent
							
								
									0c319a89d7
								
							
						
					
					
						commit
						6de624d537
					
				| 
						 | 
					@ -213,14 +213,7 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
 | 
				
			||||||
		if (!(executable instanceof Method method)) {
 | 
							if (!(executable instanceof Method method)) {
 | 
				
			||||||
			return beanSupplier.get();
 | 
								return beanSupplier.get();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		Method priorInvokedFactoryMethod = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
 | 
							return SimpleInstantiationStrategy.instantiateWithFactoryMethod(method, beanSupplier::get);
 | 
				
			||||||
		try {
 | 
					 | 
				
			||||||
			SimpleInstantiationStrategy.setCurrentlyInvokedFactoryMethod(method);
 | 
					 | 
				
			||||||
			return beanSupplier.get();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		finally {
 | 
					 | 
				
			||||||
			SimpleInstantiationStrategy.setCurrentlyInvokedFactoryMethod(priorInvokedFactoryMethod);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Nullable
 | 
						@Nullable
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,6 +27,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
 | 
				
			||||||
import org.springframework.lang.Nullable;
 | 
					import org.springframework.lang.Nullable;
 | 
				
			||||||
import org.springframework.util.ReflectionUtils;
 | 
					import org.springframework.util.ReflectionUtils;
 | 
				
			||||||
import org.springframework.util.StringUtils;
 | 
					import org.springframework.util.StringUtils;
 | 
				
			||||||
 | 
					import org.springframework.util.function.ThrowingSupplier;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Simple object instantiation strategy for use in a BeanFactory.
 | 
					 * Simple object instantiation strategy for use in a BeanFactory.
 | 
				
			||||||
| 
						 | 
					@ -59,7 +60,9 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
 | 
				
			||||||
	 * the  current value, if any.
 | 
						 * the  current value, if any.
 | 
				
			||||||
	 * @param method the factory method currently being invoked or {@code null}
 | 
						 * @param method the factory method currently being invoked or {@code null}
 | 
				
			||||||
	 * @since 6.0
 | 
						 * @since 6.0
 | 
				
			||||||
 | 
						 * @deprecated in favor of {@link #instantiateWithFactoryMethod(Method, ThrowingSupplier)}
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
 | 
						@Deprecated(since = "6.2", forRemoval = true)
 | 
				
			||||||
	public static void setCurrentlyInvokedFactoryMethod(@Nullable Method method) {
 | 
						public static void setCurrentlyInvokedFactoryMethod(@Nullable Method method) {
 | 
				
			||||||
		if (method != null) {
 | 
							if (method != null) {
 | 
				
			||||||
			currentlyInvokedFactoryMethod.set(method);
 | 
								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 <T> the type of the instance
 | 
				
			||||||
 | 
						 * @return the result of the instance supplier
 | 
				
			||||||
 | 
						 * @since 6.2
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static <T> T instantiateWithFactoryMethod(Method method, ThrowingSupplier<T> instanceSupplier) {
 | 
				
			||||||
 | 
							Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								currentlyInvokedFactoryMethod.set(method);
 | 
				
			||||||
 | 
								return instanceSupplier.get();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							finally {
 | 
				
			||||||
 | 
								if (priorInvokedFactoryMethod != null) {
 | 
				
			||||||
 | 
									currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								else {
 | 
				
			||||||
 | 
									currentlyInvokedFactoryMethod.remove();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
 | 
						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,
 | 
						public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
 | 
				
			||||||
			@Nullable Object factoryBean, Method factoryMethod, Object... args) {
 | 
								@Nullable Object factoryBean, Method factoryMethod, Object... args) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		try {
 | 
							return instantiateWithFactoryMethod(factoryMethod, () -> {
 | 
				
			||||||
			ReflectionUtils.makeAccessible(factoryMethod);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			Method priorInvokedFactoryMethod = getCurrentlyInvokedFactoryMethod();
 | 
					 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
				setCurrentlyInvokedFactoryMethod(factoryMethod);
 | 
									ReflectionUtils.makeAccessible(factoryMethod);
 | 
				
			||||||
				Object result = factoryMethod.invoke(factoryBean, args);
 | 
									Object result = factoryMethod.invoke(factoryBean, args);
 | 
				
			||||||
				if (result == null) {
 | 
									if (result == null) {
 | 
				
			||||||
					result = new NullBean();
 | 
										result = new NullBean();
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				return result;
 | 
									return result;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			finally {
 | 
								catch (IllegalArgumentException ex) {
 | 
				
			||||||
				setCurrentlyInvokedFactoryMethod(priorInvokedFactoryMethod);
 | 
									if (factoryBean != null && !factoryMethod.getDeclaringClass().isAssignableFrom(factoryBean.getClass())) {
 | 
				
			||||||
			}
 | 
										throw new BeanInstantiationException(factoryMethod,
 | 
				
			||||||
		}
 | 
												"Illegal factory instance for factory method '" + factoryMethod.getName() + "'; " +
 | 
				
			||||||
		catch (IllegalArgumentException ex) {
 | 
														"instance: " + factoryBean.getClass().getName(), ex);
 | 
				
			||||||
			if (factoryBean != null && !factoryMethod.getDeclaringClass().isAssignableFrom(factoryBean.getClass())) {
 | 
									}
 | 
				
			||||||
				throw new BeanInstantiationException(factoryMethod,
 | 
									throw new BeanInstantiationException(factoryMethod,
 | 
				
			||||||
						"Illegal factory instance for factory method '" + factoryMethod.getName() + "'; " +
 | 
											"Illegal arguments to factory method '" + factoryMethod.getName() + "'; " +
 | 
				
			||||||
						"instance: " + factoryBean.getClass().getName(), ex);
 | 
													"args: " + StringUtils.arrayToCommaDelimitedString(args), ex);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			throw new BeanInstantiationException(factoryMethod,
 | 
								catch (IllegalAccessException ex) {
 | 
				
			||||||
					"Illegal arguments to factory method '" + factoryMethod.getName() + "'; " +
 | 
									throw new BeanInstantiationException(factoryMethod,
 | 
				
			||||||
					"args: " + StringUtils.arrayToCommaDelimitedString(args), ex);
 | 
											"Cannot access factory method '" + factoryMethod.getName() + "'; is it public?", 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;
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			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());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue