Resolve AOT factory method target by bean name and reduce reflective method exposure
Includes consistent withShortcut naming and consistent bean definition flag exposure. Removes support for inner classes in alignment with standard core container behavior. Closes gh-32834
This commit is contained in:
parent
aef5143642
commit
d6e4bd7c90
|
@ -113,26 +113,34 @@ class BeanDefinitionPropertiesCodeGenerator {
|
|||
|
||||
CodeBlock generateCode(RootBeanDefinition beanDefinition) {
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
addStatementForValue(code, beanDefinition, BeanDefinition::isPrimary,
|
||||
"$L.setPrimary($L)");
|
||||
addStatementForValue(code, beanDefinition, BeanDefinition::isFallback,
|
||||
"$L.setFallback($L)");
|
||||
addStatementForValue(code, beanDefinition, BeanDefinition::getScope,
|
||||
this::hasScope, "$L.setScope($S)");
|
||||
addStatementForValue(code, beanDefinition, AbstractBeanDefinition::isBackgroundInit,
|
||||
"$L.setBackgroundInit($L)");
|
||||
addStatementForValue(code, beanDefinition, AbstractBeanDefinition::getLazyInit,
|
||||
"$L.setLazyInit($L)");
|
||||
addStatementForValue(code, beanDefinition, BeanDefinition::getDependsOn,
|
||||
this::hasDependsOn, "$L.setDependsOn($L)", this::toStringVarArgs);
|
||||
addStatementForValue(code, beanDefinition, BeanDefinition::isAutowireCandidate,
|
||||
"$L.setAutowireCandidate($L)");
|
||||
addStatementForValue(code, beanDefinition, BeanDefinition::getRole,
|
||||
this::hasRole, "$L.setRole($L)", this::toRole);
|
||||
addStatementForValue(code, beanDefinition, AbstractBeanDefinition::getLazyInit,
|
||||
"$L.setLazyInit($L)");
|
||||
addStatementForValue(code, beanDefinition, AbstractBeanDefinition::isDefaultCandidate,
|
||||
"$L.setDefaultCandidate($L)");
|
||||
addStatementForValue(code, beanDefinition, BeanDefinition::isPrimary,
|
||||
"$L.setPrimary($L)");
|
||||
addStatementForValue(code, beanDefinition, BeanDefinition::isFallback,
|
||||
"$L.setFallback($L)");
|
||||
addStatementForValue(code, beanDefinition, AbstractBeanDefinition::isSynthetic,
|
||||
"$L.setSynthetic($L)");
|
||||
addStatementForValue(code, beanDefinition, BeanDefinition::getRole,
|
||||
this::hasRole, "$L.setRole($L)", this::toRole);
|
||||
addInitDestroyMethods(code, beanDefinition, beanDefinition.getInitMethodNames(),
|
||||
"$L.setInitMethodNames($L)");
|
||||
addInitDestroyMethods(code, beanDefinition, beanDefinition.getDestroyMethodNames(),
|
||||
"$L.setDestroyMethodNames($L)");
|
||||
if (beanDefinition.getFactoryBeanName() != null) {
|
||||
addStatementForValue(code, beanDefinition, BeanDefinition::getFactoryBeanName,
|
||||
"$L.setFactoryBeanName(\"$L\")");
|
||||
}
|
||||
addConstructorArgumentValues(code, beanDefinition);
|
||||
addPropertyValues(code, beanDefinition);
|
||||
addAttributes(code, beanDefinition);
|
||||
|
@ -142,6 +150,7 @@ class BeanDefinitionPropertiesCodeGenerator {
|
|||
|
||||
private void addInitDestroyMethods(Builder code, AbstractBeanDefinition beanDefinition,
|
||||
@Nullable String[] methodNames, String format) {
|
||||
|
||||
// For Publisher-based destroy methods
|
||||
this.hints.reflection().registerType(TypeReference.of("org.reactivestreams.Publisher"));
|
||||
if (!ObjectUtils.isEmpty(methodNames)) {
|
||||
|
@ -210,7 +219,6 @@ class BeanDefinitionPropertiesCodeGenerator {
|
|||
else if (valueHolder.getType() != null) {
|
||||
code.addStatement("$L.getConstructorArgumentValues().addGenericArgumentValue($L, $S)",
|
||||
BEAN_DEFINITION_VARIABLE, valueCode, valueHolder.getType());
|
||||
|
||||
}
|
||||
else {
|
||||
code.addStatement("$L.getConstructorArgumentValues().addGenericArgumentValue($L)",
|
||||
|
@ -224,7 +232,8 @@ class BeanDefinitionPropertiesCodeGenerator {
|
|||
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
|
||||
if (!propertyValues.isEmpty()) {
|
||||
Class<?> infrastructureType = getInfrastructureType(beanDefinition);
|
||||
Map<String, Method> writeMethods = (infrastructureType != Object.class) ? getWriteMethods(infrastructureType) : Collections.emptyMap();
|
||||
Map<String, Method> writeMethods = (infrastructureType != Object.class ?
|
||||
getWriteMethods(infrastructureType) : Collections.emptyMap());
|
||||
for (PropertyValue propertyValue : propertyValues) {
|
||||
String name = propertyValue.getName();
|
||||
CodeBlock valueCode = generateValue(name, propertyValue.getValue());
|
||||
|
@ -266,8 +275,8 @@ class BeanDefinitionPropertiesCodeGenerator {
|
|||
}
|
||||
|
||||
private CodeBlock generateValue(@Nullable String name, @Nullable Object value) {
|
||||
PropertyNamesStack.push(name);
|
||||
try {
|
||||
PropertyNamesStack.push(name);
|
||||
return this.valueCodeGenerator.generateCode(value);
|
||||
}
|
||||
finally {
|
||||
|
@ -308,8 +317,7 @@ class BeanDefinitionPropertiesCodeGenerator {
|
|||
}
|
||||
|
||||
private boolean hasScope(String defaultValue, String actualValue) {
|
||||
return StringUtils.hasText(actualValue) &&
|
||||
!ConfigurableBeanFactory.SCOPE_SINGLETON.equals(actualValue);
|
||||
return (StringUtils.hasText(actualValue) && !ConfigurableBeanFactory.SCOPE_SINGLETON.equals(actualValue));
|
||||
}
|
||||
|
||||
private boolean hasDependsOn(String[] defaultValue, String[] actualValue) {
|
||||
|
@ -335,8 +343,7 @@ class BeanDefinitionPropertiesCodeGenerator {
|
|||
}
|
||||
|
||||
private <B extends BeanDefinition, T> void addStatementForValue(
|
||||
CodeBlock.Builder code, BeanDefinition beanDefinition,
|
||||
Function<B, T> getter, String format) {
|
||||
CodeBlock.Builder code, BeanDefinition beanDefinition, Function<B, T> getter, String format) {
|
||||
|
||||
addStatementForValue(code, beanDefinition, getter,
|
||||
(defaultValue, actualValue) -> !Objects.equals(defaultValue, actualValue), format);
|
||||
|
@ -351,9 +358,8 @@ class BeanDefinitionPropertiesCodeGenerator {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <B extends BeanDefinition, T> void addStatementForValue(
|
||||
CodeBlock.Builder code, BeanDefinition beanDefinition,
|
||||
Function<B, T> getter, BiPredicate<T, T> filter, String format,
|
||||
Function<T, Object> formatter) {
|
||||
CodeBlock.Builder code, BeanDefinition beanDefinition, Function<B, T> getter,
|
||||
BiPredicate<T, T> filter, String format, Function<T, Object> formatter) {
|
||||
|
||||
T defaultValue = getter.apply((B) DEFAULT_BEAN_DEFINITION);
|
||||
T actualValue = getter.apply((B) beanDefinition);
|
||||
|
@ -363,9 +369,8 @@ class BeanDefinitionPropertiesCodeGenerator {
|
|||
}
|
||||
|
||||
/**
|
||||
* Cast the specified {@code valueCode} to the specified {@code castType} if
|
||||
* the {@code castNecessary} is {@code true}. Otherwise return the valueCode
|
||||
* as is.
|
||||
* Cast the specified {@code valueCode} to the specified {@code castType} if the
|
||||
* {@code castNecessary} is {@code true}. Otherwise, return the valueCode as-is.
|
||||
* @param castNecessary whether a cast is necessary
|
||||
* @param castType the type to cast to
|
||||
* @param valueCode the code for the value
|
||||
|
|
|
@ -32,9 +32,7 @@ import org.springframework.beans.BeanInstantiationException;
|
|||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
|
@ -49,7 +47,6 @@ import org.springframework.lang.Nullable;
|
|||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.function.ThrowingBiFunction;
|
||||
import org.springframework.util.function.ThrowingFunction;
|
||||
|
@ -79,6 +76,7 @@ import org.springframework.util.function.ThrowingSupplier;
|
|||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 6.0
|
||||
* @param <T> the type of instance supplied by this supplier
|
||||
* @see AutowiredArguments
|
||||
|
@ -88,19 +86,24 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
|||
private final ExecutableLookup lookup;
|
||||
|
||||
@Nullable
|
||||
private final ThrowingBiFunction<RegisteredBean, AutowiredArguments, T> generator;
|
||||
private final ThrowingFunction<RegisteredBean, T> generatorWithoutArguments;
|
||||
|
||||
@Nullable
|
||||
private final String[] shortcuts;
|
||||
private final ThrowingBiFunction<RegisteredBean, AutowiredArguments, T> generatorWithArguments;
|
||||
|
||||
@Nullable
|
||||
private final String[] shortcutBeanNames;
|
||||
|
||||
|
||||
private BeanInstanceSupplier(ExecutableLookup lookup,
|
||||
@Nullable ThrowingBiFunction<RegisteredBean, AutowiredArguments, T> generator,
|
||||
@Nullable String[] shortcuts) {
|
||||
@Nullable ThrowingFunction<RegisteredBean, T> generatorWithoutArguments,
|
||||
@Nullable ThrowingBiFunction<RegisteredBean, AutowiredArguments, T> generatorWithArguments,
|
||||
@Nullable String[] shortcutBeanNames) {
|
||||
|
||||
this.lookup = lookup;
|
||||
this.generator = generator;
|
||||
this.shortcuts = shortcuts;
|
||||
this.generatorWithoutArguments = generatorWithoutArguments;
|
||||
this.generatorWithArguments = generatorWithArguments;
|
||||
this.shortcutBeanNames = shortcutBeanNames;
|
||||
}
|
||||
|
||||
|
||||
|
@ -114,7 +117,7 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
|||
public static <T> BeanInstanceSupplier<T> forConstructor(Class<?>... parameterTypes) {
|
||||
Assert.notNull(parameterTypes, "'parameterTypes' must not be null");
|
||||
Assert.noNullElements(parameterTypes, "'parameterTypes' must not contain null elements");
|
||||
return new BeanInstanceSupplier<>(new ConstructorLookup(parameterTypes), null, null);
|
||||
return new BeanInstanceSupplier<>(new ConstructorLookup(parameterTypes), null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -135,7 +138,7 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
|||
Assert.noNullElements(parameterTypes, "'parameterTypes' must not contain null elements");
|
||||
return new BeanInstanceSupplier<>(
|
||||
new FactoryMethodLookup(declaringClass, methodName, parameterTypes),
|
||||
null, null);
|
||||
null, null, null);
|
||||
}
|
||||
|
||||
|
||||
|
@ -151,11 +154,9 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
|||
* instantiate the underlying bean
|
||||
* @return a new {@link BeanInstanceSupplier} instance with the specified generator
|
||||
*/
|
||||
public BeanInstanceSupplier<T> withGenerator(
|
||||
ThrowingBiFunction<RegisteredBean, AutowiredArguments, T> generator) {
|
||||
|
||||
public BeanInstanceSupplier<T> withGenerator(ThrowingBiFunction<RegisteredBean, AutowiredArguments, T> generator) {
|
||||
Assert.notNull(generator, "'generator' must not be null");
|
||||
return new BeanInstanceSupplier<>(this.lookup, generator, this.shortcuts);
|
||||
return new BeanInstanceSupplier<>(this.lookup, null, generator, this.shortcutBeanNames);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -167,8 +168,7 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
|||
*/
|
||||
public BeanInstanceSupplier<T> withGenerator(ThrowingFunction<RegisteredBean, T> generator) {
|
||||
Assert.notNull(generator, "'generator' must not be null");
|
||||
return new BeanInstanceSupplier<>(this.lookup,
|
||||
(registeredBean, args) -> generator.apply(registeredBean), this.shortcuts);
|
||||
return new BeanInstanceSupplier<>(this.lookup, generator, null, this.shortcutBeanNames);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -181,50 +181,83 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
|||
@Deprecated(since = "6.0.11", forRemoval = true)
|
||||
public BeanInstanceSupplier<T> withGenerator(ThrowingSupplier<T> generator) {
|
||||
Assert.notNull(generator, "'generator' must not be null");
|
||||
return new BeanInstanceSupplier<>(this.lookup,
|
||||
(registeredBean, args) -> generator.get(), this.shortcuts);
|
||||
return new BeanInstanceSupplier<>(this.lookup, registeredBean -> generator.get(),
|
||||
null, this.shortcutBeanNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link BeanInstanceSupplier} instance
|
||||
* that uses direct bean name injection shortcuts for specific parameters.
|
||||
* @param beanNames the bean names to use as shortcuts (aligned with the
|
||||
* constructor or factory method parameters)
|
||||
* @return a new {@link BeanInstanceSupplier} instance
|
||||
* that uses the shortcuts
|
||||
* @deprecated in favor of {@link #withShortcut(String...)}
|
||||
*/
|
||||
@Deprecated(since = "6.2", forRemoval = true)
|
||||
public BeanInstanceSupplier<T> withShortcuts(String... beanNames) {
|
||||
return new BeanInstanceSupplier<>(this.lookup, this.generator, beanNames);
|
||||
return withShortcut(beanNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link BeanInstanceSupplier} instance that uses
|
||||
* direct bean name injection shortcuts for specific parameters.
|
||||
* @param beanNames the bean names to use as shortcut (aligned with the
|
||||
* constructor or factory method parameters)
|
||||
* @return a new {@link BeanInstanceSupplier} instance that uses the
|
||||
* given shortcut bean names
|
||||
* @since 6.2
|
||||
*/
|
||||
public BeanInstanceSupplier<T> withShortcut(String... beanNames) {
|
||||
return new BeanInstanceSupplier<>(
|
||||
this.lookup, this.generatorWithoutArguments, this.generatorWithArguments, beanNames);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T get(RegisteredBean registeredBean) throws Exception {
|
||||
public T get(RegisteredBean registeredBean) {
|
||||
Assert.notNull(registeredBean, "'registeredBean' must not be null");
|
||||
Executable executable = this.lookup.get(registeredBean);
|
||||
AutowiredArguments arguments = resolveArguments(registeredBean, executable);
|
||||
if (this.generator != null) {
|
||||
return invokeBeanSupplier(executable, () -> this.generator.apply(registeredBean, arguments));
|
||||
if (this.generatorWithoutArguments != null) {
|
||||
Executable executable = getFactoryMethodForGenerator();
|
||||
return invokeBeanSupplier(executable, () -> this.generatorWithoutArguments.apply(registeredBean));
|
||||
}
|
||||
else if (this.generatorWithArguments != null) {
|
||||
Executable executable = getFactoryMethodForGenerator();
|
||||
AutowiredArguments arguments = resolveArguments(registeredBean,
|
||||
executable != null ? executable : this.lookup.get(registeredBean));
|
||||
return invokeBeanSupplier(executable, () -> this.generatorWithArguments.apply(registeredBean, arguments));
|
||||
}
|
||||
else {
|
||||
Executable executable = this.lookup.get(registeredBean);
|
||||
Object[] arguments = resolveArguments(registeredBean, executable).toArray();
|
||||
return invokeBeanSupplier(executable, () -> (T) instantiate(registeredBean, executable, arguments));
|
||||
}
|
||||
return invokeBeanSupplier(executable,
|
||||
() -> instantiate(registeredBean.getBeanFactory(), executable, arguments.toArray()));
|
||||
}
|
||||
|
||||
private T invokeBeanSupplier(Executable executable, ThrowingSupplier<T> beanSupplier) {
|
||||
if (!(executable instanceof Method method)) {
|
||||
return beanSupplier.get();
|
||||
}
|
||||
return SimpleInstantiationStrategy.instantiateWithFactoryMethod(method, beanSupplier::get);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Nullable
|
||||
public Method getFactoryMethod() {
|
||||
// Cached factory method retrieval for qualifier introspection etc.
|
||||
if (this.lookup instanceof FactoryMethodLookup factoryMethodLookup) {
|
||||
return factoryMethodLookup.get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Method getFactoryMethodForGenerator() {
|
||||
// Avoid unnecessary currentlyInvokedFactoryMethod exposure outside of full configuration classes.
|
||||
if (this.lookup instanceof FactoryMethodLookup factoryMethodLookup &&
|
||||
factoryMethodLookup.declaringClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
|
||||
return factoryMethodLookup.get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private T invokeBeanSupplier(@Nullable Executable executable, ThrowingSupplier<T> beanSupplier) {
|
||||
if (executable instanceof Method method) {
|
||||
return SimpleInstantiationStrategy.instantiateWithFactoryMethod(method, beanSupplier);
|
||||
}
|
||||
return beanSupplier.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve arguments for the specified registered bean.
|
||||
* @param registeredBean the registered bean
|
||||
|
@ -236,26 +269,22 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
|||
}
|
||||
|
||||
private AutowiredArguments resolveArguments(RegisteredBean registeredBean, Executable executable) {
|
||||
Assert.isInstanceOf(AbstractAutowireCapableBeanFactory.class, registeredBean.getBeanFactory());
|
||||
|
||||
int startIndex = (executable instanceof Constructor<?> constructor &&
|
||||
ClassUtils.isInnerClass(constructor.getDeclaringClass())) ? 1 : 0;
|
||||
int parameterCount = executable.getParameterCount();
|
||||
Object[] resolved = new Object[parameterCount - startIndex];
|
||||
Assert.isTrue(this.shortcuts == null || this.shortcuts.length == resolved.length,
|
||||
Object[] resolved = new Object[parameterCount];
|
||||
Assert.isTrue(this.shortcutBeanNames == null || this.shortcutBeanNames.length == resolved.length,
|
||||
() -> "'shortcuts' must contain " + resolved.length + " elements");
|
||||
|
||||
ValueHolder[] argumentValues = resolveArgumentValues(registeredBean, executable);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(resolved.length * 2);
|
||||
for (int i = startIndex; i < parameterCount; i++) {
|
||||
for (int i = 0; i < parameterCount; i++) {
|
||||
MethodParameter parameter = getMethodParameter(executable, i);
|
||||
DependencyDescriptor descriptor = new DependencyDescriptor(parameter, true);
|
||||
String shortcut = (this.shortcuts != null ? this.shortcuts[i - startIndex] : null);
|
||||
String shortcut = (this.shortcutBeanNames != null ? this.shortcutBeanNames[i] : null);
|
||||
if (shortcut != null) {
|
||||
descriptor = new ShortcutDependencyDescriptor(descriptor, shortcut);
|
||||
}
|
||||
ValueHolder argumentValue = argumentValues[i];
|
||||
resolved[i - startIndex] = resolveAutowiredArgument(
|
||||
resolved[i] = resolveAutowiredArgument(
|
||||
registeredBean, descriptor, argumentValue, autowiredBeanNames);
|
||||
}
|
||||
registerDependentBeans(registeredBean.getBeanFactory(), registeredBean.getBeanName(), autowiredBeanNames);
|
||||
|
@ -339,62 +368,30 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private T instantiate(ConfigurableBeanFactory beanFactory, Executable executable, Object[] args) {
|
||||
private Object instantiate(RegisteredBean registeredBean, Executable executable, Object[] args) {
|
||||
if (executable instanceof Constructor<?> constructor) {
|
||||
try {
|
||||
return (T) instantiate(constructor, args);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new BeanInstantiationException(constructor, ex.getMessage(), ex);
|
||||
}
|
||||
return BeanUtils.instantiateClass(constructor, args);
|
||||
}
|
||||
if (executable instanceof Method method) {
|
||||
try {
|
||||
return (T) instantiate(beanFactory, method, args);
|
||||
Object target = null;
|
||||
String factoryBeanName = registeredBean.getMergedBeanDefinition().getFactoryBeanName();
|
||||
if (factoryBeanName != null) {
|
||||
target = registeredBean.getBeanFactory().getBean(factoryBeanName, method.getDeclaringClass());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
else if (!Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("Cannot invoke instance method without factoryBeanName: " + method);
|
||||
}
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
return method.invoke(target, args);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanInstantiationException(method, ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Unsupported executable " + executable.getClass().getName());
|
||||
}
|
||||
|
||||
private Object instantiate(Constructor<?> constructor, Object[] args) throws Exception {
|
||||
Class<?> declaringClass = constructor.getDeclaringClass();
|
||||
if (ClassUtils.isInnerClass(declaringClass)) {
|
||||
Object enclosingInstance = createInstance(declaringClass.getEnclosingClass());
|
||||
args = ObjectUtils.addObjectToArray(args, enclosingInstance, 0);
|
||||
}
|
||||
return BeanUtils.instantiateClass(constructor, args);
|
||||
}
|
||||
|
||||
private Object instantiate(ConfigurableBeanFactory beanFactory, Method method, Object[] args) throws Exception {
|
||||
Object target = getFactoryMethodTarget(beanFactory, method);
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
return method.invoke(target, args);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object getFactoryMethodTarget(BeanFactory beanFactory, Method method) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
return null;
|
||||
}
|
||||
Class<?> declaringClass = method.getDeclaringClass();
|
||||
return beanFactory.getBean(declaringClass);
|
||||
}
|
||||
|
||||
private Object createInstance(Class<?> clazz) throws Exception {
|
||||
if (!ClassUtils.isInnerClass(clazz)) {
|
||||
Constructor<?> constructor = clazz.getDeclaredConstructor();
|
||||
ReflectionUtils.makeAccessible(constructor);
|
||||
return constructor.newInstance();
|
||||
}
|
||||
Class<?> enclosingClass = clazz.getEnclosingClass();
|
||||
Constructor<?> constructor = clazz.getDeclaredConstructor(enclosingClass);
|
||||
return constructor.newInstance(createInstance(enclosingClass));
|
||||
}
|
||||
|
||||
|
||||
private static String toCommaSeparatedNames(Class<?>... parameterTypes) {
|
||||
return Arrays.stream(parameterTypes).map(Class::getName).collect(Collectors.joining(", "));
|
||||
|
@ -423,12 +420,9 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
|||
|
||||
@Override
|
||||
public Executable get(RegisteredBean registeredBean) {
|
||||
Class<?> beanClass = registeredBean.getBeanClass();
|
||||
Class<?> beanClass = registeredBean.getMergedBeanDefinition().getBeanClass();
|
||||
try {
|
||||
Class<?>[] actualParameterTypes = (!ClassUtils.isInnerClass(beanClass)) ?
|
||||
this.parameterTypes : ObjectUtils.addObjectToArray(
|
||||
this.parameterTypes, beanClass.getEnclosingClass(), 0);
|
||||
return beanClass.getDeclaredConstructor(actualParameterTypes);
|
||||
return beanClass.getDeclaredConstructor(this.parameterTypes);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalArgumentException(
|
||||
|
@ -454,6 +448,9 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
|||
|
||||
private final Class<?>[] parameterTypes;
|
||||
|
||||
@Nullable
|
||||
private volatile Method resolvedMethod;
|
||||
|
||||
FactoryMethodLookup(Class<?> declaringClass, String methodName, Class<?>[] parameterTypes) {
|
||||
this.declaringClass = declaringClass;
|
||||
this.methodName = methodName;
|
||||
|
@ -466,8 +463,13 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
|||
}
|
||||
|
||||
Method get() {
|
||||
Method method = ReflectionUtils.findMethod(this.declaringClass, this.methodName, this.parameterTypes);
|
||||
Assert.notNull(method, () -> "%s cannot be found".formatted(this));
|
||||
Method method = this.resolvedMethod;
|
||||
if (method == null) {
|
||||
method = ReflectionUtils.findMethod(
|
||||
ClassUtils.getUserClass(this.declaringClass), this.methodName, this.parameterTypes);
|
||||
Assert.notNull(method, () -> "%s cannot be found".formatted(this));
|
||||
this.resolvedMethod = method;
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
|
|||
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
RootBeanDefinition mbd = this.registeredBean.getMergedBeanDefinition();
|
||||
Class<?> beanClass = (mbd.hasBeanClass() ? ClassUtils.getUserClass(mbd.getBeanClass()) : null);
|
||||
Class<?> beanClass = (mbd.hasBeanClass() ? mbd.getBeanClass() : null);
|
||||
CodeBlock beanClassCode = generateBeanClassCode(
|
||||
beanRegistrationCode.getClassName().packageName(),
|
||||
(beanClass != null ? beanClass : beanType.toClass()));
|
||||
|
@ -158,7 +158,7 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
|
|||
if (beanClass != null && this.registeredBean.getMergedBeanDefinition().getFactoryMethodName() != null) {
|
||||
return true;
|
||||
}
|
||||
return (beanClass != null && !beanType.toClass().equals(beanClass));
|
||||
return (beanClass != null && !beanType.toClass().equals(ClassUtils.getUserClass(beanClass)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,7 +20,6 @@ import java.lang.reflect.Constructor;
|
|||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Arrays;
|
||||
|
@ -55,6 +54,7 @@ import org.springframework.javapoet.CodeBlock;
|
|||
import org.springframework.javapoet.CodeBlock.Builder;
|
||||
import org.springframework.javapoet.MethodSpec;
|
||||
import org.springframework.javapoet.ParameterizedTypeName;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.function.ThrowingSupplier;
|
||||
|
||||
|
@ -83,9 +83,8 @@ public class InstanceSupplierCodeGenerator {
|
|||
|
||||
private static final String ARGS_PARAMETER_NAME = "args";
|
||||
|
||||
private static final javax.lang.model.element.Modifier[] PRIVATE_STATIC = {
|
||||
javax.lang.model.element.Modifier.PRIVATE,
|
||||
javax.lang.model.element.Modifier.STATIC };
|
||||
private static final javax.lang.model.element.Modifier[] PRIVATE_STATIC =
|
||||
{javax.lang.model.element.Modifier.PRIVATE, javax.lang.model.element.Modifier.STATIC};
|
||||
|
||||
private static final CodeBlock NO_ARGS = CodeBlock.of("");
|
||||
|
||||
|
@ -165,28 +164,26 @@ public class InstanceSupplierCodeGenerator {
|
|||
String beanName = registeredBean.getBeanName();
|
||||
Class<?> beanClass = registeredBean.getBeanClass();
|
||||
Class<?> declaringClass = constructor.getDeclaringClass();
|
||||
boolean dependsOnBean = ClassUtils.isInnerClass(declaringClass);
|
||||
|
||||
Visibility accessVisibility = getAccessVisibility(registeredBean, constructor);
|
||||
if (KotlinDetector.isKotlinReflectPresent() && KotlinDelegate.hasConstructorWithOptionalParameter(beanClass)) {
|
||||
return generateCodeForInaccessibleConstructor(beanName, beanClass, constructor,
|
||||
dependsOnBean, hints -> hints.registerType(beanClass, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
|
||||
hints -> hints.registerType(beanClass, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
|
||||
}
|
||||
else if (accessVisibility != Visibility.PRIVATE) {
|
||||
return generateCodeForAccessibleConstructor(beanName, beanClass, constructor,
|
||||
dependsOnBean, declaringClass);
|
||||
return generateCodeForAccessibleConstructor(beanName, beanClass, constructor, declaringClass);
|
||||
}
|
||||
return generateCodeForInaccessibleConstructor(beanName, beanClass, constructor, dependsOnBean,
|
||||
return generateCodeForInaccessibleConstructor(beanName, beanClass, constructor,
|
||||
hints -> hints.registerConstructor(constructor, ExecutableMode.INVOKE));
|
||||
}
|
||||
|
||||
private CodeBlock generateCodeForAccessibleConstructor(String beanName, Class<?> beanClass,
|
||||
Constructor<?> constructor, boolean dependsOnBean, Class<?> declaringClass) {
|
||||
Constructor<?> constructor, Class<?> declaringClass) {
|
||||
|
||||
this.generationContext.getRuntimeHints().reflection().registerConstructor(
|
||||
constructor, ExecutableMode.INTROSPECT);
|
||||
|
||||
if (!dependsOnBean && constructor.getParameterCount() == 0) {
|
||||
if (constructor.getParameterCount() == 0) {
|
||||
if (!this.allowDirectSupplierShortcut) {
|
||||
return CodeBlock.of("$T.using($T::new)", InstanceSupplier.class, declaringClass);
|
||||
}
|
||||
|
@ -197,13 +194,13 @@ public class InstanceSupplierCodeGenerator {
|
|||
}
|
||||
|
||||
GeneratedMethod generatedMethod = generateGetInstanceSupplierMethod(method ->
|
||||
buildGetInstanceMethodForConstructor(method, beanName, beanClass, constructor,
|
||||
declaringClass, dependsOnBean, PRIVATE_STATIC));
|
||||
buildGetInstanceMethodForConstructor(
|
||||
method, beanName, beanClass, constructor, declaringClass, PRIVATE_STATIC));
|
||||
return generateReturnStatement(generatedMethod);
|
||||
}
|
||||
|
||||
private CodeBlock generateCodeForInaccessibleConstructor(String beanName, Class<?> beanClass,
|
||||
Constructor<?> constructor, boolean dependsOnBean, Consumer<ReflectionHints> hints) {
|
||||
Constructor<?> constructor, Consumer<ReflectionHints> hints) {
|
||||
|
||||
CodeWarnings codeWarnings = new CodeWarnings();
|
||||
codeWarnings.detectDeprecation(beanClass, constructor)
|
||||
|
@ -215,8 +212,7 @@ public class InstanceSupplierCodeGenerator {
|
|||
method.addModifiers(PRIVATE_STATIC);
|
||||
codeWarnings.suppress(method);
|
||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
|
||||
int parameterOffset = (!dependsOnBean) ? 0 : 1;
|
||||
method.addStatement(generateResolverForConstructor(beanClass, constructor, parameterOffset));
|
||||
method.addStatement(generateResolverForConstructor(beanClass, constructor));
|
||||
});
|
||||
|
||||
return generateReturnStatement(generatedMethod);
|
||||
|
@ -224,7 +220,7 @@ public class InstanceSupplierCodeGenerator {
|
|||
|
||||
private void buildGetInstanceMethodForConstructor(MethodSpec.Builder method,
|
||||
String beanName, Class<?> beanClass, Constructor<?> constructor, Class<?> declaringClass,
|
||||
boolean dependsOnBean, javax.lang.model.element.Modifier... modifiers) {
|
||||
javax.lang.model.element.Modifier... modifiers) {
|
||||
|
||||
CodeWarnings codeWarnings = new CodeWarnings();
|
||||
codeWarnings.detectDeprecation(beanClass, constructor, declaringClass)
|
||||
|
@ -234,72 +230,58 @@ public class InstanceSupplierCodeGenerator {
|
|||
codeWarnings.suppress(method);
|
||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
|
||||
|
||||
int parameterOffset = (!dependsOnBean) ? 0 : 1;
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
code.add(generateResolverForConstructor(beanClass, constructor, parameterOffset));
|
||||
code.add(generateResolverForConstructor(beanClass, constructor));
|
||||
boolean hasArguments = constructor.getParameterCount() > 0;
|
||||
|
||||
CodeBlock arguments = hasArguments ?
|
||||
new AutowiredArgumentsCodeGenerator(declaringClass, constructor)
|
||||
.generateCode(constructor.getParameterTypes(), parameterOffset)
|
||||
.generateCode(constructor.getParameterTypes())
|
||||
: NO_ARGS;
|
||||
|
||||
CodeBlock newInstance = generateNewInstanceCodeForConstructor(dependsOnBean, declaringClass, arguments);
|
||||
CodeBlock newInstance = generateNewInstanceCodeForConstructor(declaringClass, arguments);
|
||||
code.add(generateWithGeneratorCode(hasArguments, newInstance));
|
||||
method.addStatement(code.build());
|
||||
}
|
||||
|
||||
private CodeBlock generateResolverForConstructor(Class<?> beanClass,
|
||||
Constructor<?> constructor, int parameterOffset) {
|
||||
|
||||
CodeBlock parameterTypes = generateParameterTypesCode(constructor.getParameterTypes(), parameterOffset);
|
||||
private CodeBlock generateResolverForConstructor(Class<?> beanClass, Constructor<?> constructor) {
|
||||
CodeBlock parameterTypes = generateParameterTypesCode(constructor.getParameterTypes());
|
||||
return CodeBlock.of("return $T.<$T>forConstructor($L)", BeanInstanceSupplier.class, beanClass, parameterTypes);
|
||||
}
|
||||
|
||||
private CodeBlock generateNewInstanceCodeForConstructor(boolean dependsOnBean,
|
||||
Class<?> declaringClass, CodeBlock args) {
|
||||
|
||||
if (!dependsOnBean) {
|
||||
return CodeBlock.of("new $T($L)", declaringClass, args);
|
||||
}
|
||||
|
||||
return CodeBlock.of("$L.getBeanFactory().getBean($T.class).new $L($L)",
|
||||
REGISTERED_BEAN_PARAMETER_NAME, declaringClass.getEnclosingClass(),
|
||||
declaringClass.getSimpleName(), args);
|
||||
private CodeBlock generateNewInstanceCodeForConstructor(Class<?> declaringClass, CodeBlock args) {
|
||||
return CodeBlock.of("new $T($L)", declaringClass, args);
|
||||
}
|
||||
|
||||
private CodeBlock generateCodeForFactoryMethod(RegisteredBean registeredBean, Method factoryMethod, Class<?> targetClass) {
|
||||
String beanName = registeredBean.getBeanName();
|
||||
Class<?> targetClassToUse = ClassUtils.getUserClass(targetClass);
|
||||
boolean dependsOnBean = !Modifier.isStatic(factoryMethod.getModifiers());
|
||||
private CodeBlock generateCodeForFactoryMethod(
|
||||
RegisteredBean registeredBean, Method factoryMethod, Class<?> targetClass) {
|
||||
|
||||
Visibility accessVisibility = getAccessVisibility(registeredBean, factoryMethod);
|
||||
if (accessVisibility != Visibility.PRIVATE) {
|
||||
return generateCodeForAccessibleFactoryMethod(
|
||||
beanName, factoryMethod, targetClassToUse, dependsOnBean);
|
||||
return generateCodeForAccessibleFactoryMethod(registeredBean.getBeanName(), factoryMethod, targetClass,
|
||||
registeredBean.getMergedBeanDefinition().getFactoryBeanName());
|
||||
}
|
||||
return generateCodeForInaccessibleFactoryMethod(beanName, factoryMethod, targetClassToUse);
|
||||
return generateCodeForInaccessibleFactoryMethod(registeredBean.getBeanName(), factoryMethod, targetClass);
|
||||
}
|
||||
|
||||
private CodeBlock generateCodeForAccessibleFactoryMethod(String beanName,
|
||||
Method factoryMethod, Class<?> targetClass, boolean dependsOnBean) {
|
||||
Method factoryMethod, Class<?> targetClass, @Nullable String factoryBeanName) {
|
||||
|
||||
this.generationContext.getRuntimeHints().reflection().registerMethod(
|
||||
factoryMethod, ExecutableMode.INTROSPECT);
|
||||
this.generationContext.getRuntimeHints().reflection().registerMethod(factoryMethod, ExecutableMode.INTROSPECT);
|
||||
|
||||
if (!dependsOnBean && factoryMethod.getParameterCount() == 0) {
|
||||
if (factoryBeanName == null && factoryMethod.getParameterCount() == 0) {
|
||||
Class<?> suppliedType = ClassUtils.resolvePrimitiveIfNecessary(factoryMethod.getReturnType());
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
code.add("$T.<$T>forFactoryMethod($T.class, $S)", BeanInstanceSupplier.class,
|
||||
suppliedType, targetClass, factoryMethod.getName());
|
||||
code.add(".withGenerator(($L) -> $T.$L())", REGISTERED_BEAN_PARAMETER_NAME,
|
||||
targetClass, factoryMethod.getName());
|
||||
ClassUtils.getUserClass(targetClass), factoryMethod.getName());
|
||||
return code.build();
|
||||
}
|
||||
|
||||
GeneratedMethod getInstanceMethod = generateGetInstanceSupplierMethod(method ->
|
||||
buildGetInstanceMethodForFactoryMethod(method, beanName, factoryMethod,
|
||||
targetClass, dependsOnBean, PRIVATE_STATIC));
|
||||
targetClass, factoryBeanName, PRIVATE_STATIC));
|
||||
return generateReturnStatement(getInstanceMethod);
|
||||
}
|
||||
|
||||
|
@ -320,12 +302,12 @@ public class InstanceSupplierCodeGenerator {
|
|||
|
||||
private void buildGetInstanceMethodForFactoryMethod(MethodSpec.Builder method,
|
||||
String beanName, Method factoryMethod, Class<?> targetClass,
|
||||
boolean dependsOnBean, javax.lang.model.element.Modifier... modifiers) {
|
||||
@Nullable String factoryBeanName, javax.lang.model.element.Modifier... modifiers) {
|
||||
|
||||
String factoryMethodName = factoryMethod.getName();
|
||||
Class<?> suppliedType = ClassUtils.resolvePrimitiveIfNecessary(factoryMethod.getReturnType());
|
||||
CodeWarnings codeWarnings = new CodeWarnings();
|
||||
codeWarnings.detectDeprecation(targetClass, factoryMethod, suppliedType)
|
||||
codeWarnings.detectDeprecation(ClassUtils.getUserClass(targetClass), factoryMethod, suppliedType)
|
||||
.detectDeprecation(Arrays.stream(factoryMethod.getParameters()).map(Parameter::getType));
|
||||
|
||||
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
||||
|
@ -339,12 +321,12 @@ public class InstanceSupplierCodeGenerator {
|
|||
|
||||
boolean hasArguments = factoryMethod.getParameterCount() > 0;
|
||||
CodeBlock arguments = hasArguments ?
|
||||
new AutowiredArgumentsCodeGenerator(targetClass, factoryMethod)
|
||||
new AutowiredArgumentsCodeGenerator(ClassUtils.getUserClass(targetClass), factoryMethod)
|
||||
.generateCode(factoryMethod.getParameterTypes())
|
||||
: NO_ARGS;
|
||||
|
||||
CodeBlock newInstance = generateNewInstanceCodeForMethod(
|
||||
dependsOnBean, targetClass, factoryMethodName, arguments);
|
||||
factoryBeanName, ClassUtils.getUserClass(targetClass), factoryMethodName, arguments);
|
||||
code.add(generateWithGeneratorCode(hasArguments, newInstance));
|
||||
method.addStatement(code.build());
|
||||
}
|
||||
|
@ -357,19 +339,19 @@ public class InstanceSupplierCodeGenerator {
|
|||
BeanInstanceSupplier.class, suppliedType, targetClass, factoryMethodName);
|
||||
}
|
||||
|
||||
CodeBlock parameterTypes = generateParameterTypesCode(factoryMethod.getParameterTypes(), 0);
|
||||
CodeBlock parameterTypes = generateParameterTypesCode(factoryMethod.getParameterTypes());
|
||||
return CodeBlock.of("return $T.<$T>forFactoryMethod($T.class, $S, $L)",
|
||||
BeanInstanceSupplier.class, suppliedType, targetClass, factoryMethodName, parameterTypes);
|
||||
}
|
||||
|
||||
private CodeBlock generateNewInstanceCodeForMethod(boolean dependsOnBean,
|
||||
private CodeBlock generateNewInstanceCodeForMethod(@Nullable String factoryBeanName,
|
||||
Class<?> targetClass, String factoryMethodName, CodeBlock args) {
|
||||
|
||||
if (!dependsOnBean) {
|
||||
if (factoryBeanName == null) {
|
||||
return CodeBlock.of("$T.$L($L)", targetClass, factoryMethodName, args);
|
||||
}
|
||||
return CodeBlock.of("$L.getBeanFactory().getBean($T.class).$L($L)",
|
||||
REGISTERED_BEAN_PARAMETER_NAME, targetClass, factoryMethodName, args);
|
||||
return CodeBlock.of("$L.getBeanFactory().getBean(\"$L\", $T.class).$L($L)",
|
||||
REGISTERED_BEAN_PARAMETER_NAME, factoryBeanName, targetClass, factoryMethodName, args);
|
||||
}
|
||||
|
||||
private CodeBlock generateReturnStatement(GeneratedMethod generatedMethod) {
|
||||
|
@ -395,10 +377,10 @@ public class InstanceSupplierCodeGenerator {
|
|||
return AccessControl.lowest(beanTypeAccessControl, memberAccessControl).getVisibility();
|
||||
}
|
||||
|
||||
private CodeBlock generateParameterTypesCode(Class<?>[] parameterTypes, int offset) {
|
||||
private CodeBlock generateParameterTypesCode(Class<?>[] parameterTypes) {
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
for (int i = offset; i < parameterTypes.length; i++) {
|
||||
code.add(i != offset ? ", " : "");
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
code.add(i > 0 ? ", " : "");
|
||||
code.add("$T.class", parameterTypes[i]);
|
||||
}
|
||||
return code.build();
|
||||
|
|
|
@ -375,7 +375,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
|||
if (returnType != null) {
|
||||
return returnType;
|
||||
}
|
||||
Method factoryMethod = this.factoryMethodToIntrospect;
|
||||
Method factoryMethod = getResolvedFactoryMethod();
|
||||
if (factoryMethod != null) {
|
||||
return ResolvableType.forMethodReturnType(factoryMethod);
|
||||
}
|
||||
|
@ -453,17 +453,12 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
|||
*/
|
||||
@Nullable
|
||||
public Method getResolvedFactoryMethod() {
|
||||
return this.factoryMethodToIntrospect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInstanceSupplier(@Nullable Supplier<?> supplier) {
|
||||
super.setInstanceSupplier(supplier);
|
||||
Method factoryMethod = (supplier instanceof InstanceSupplier<?> instanceSupplier ?
|
||||
instanceSupplier.getFactoryMethod() : null);
|
||||
if (factoryMethod != null) {
|
||||
setResolvedFactoryMethod(factoryMethod);
|
||||
Method factoryMethod = this.factoryMethodToIntrospect;
|
||||
if (factoryMethod == null &&
|
||||
getInstanceSupplier() instanceof InstanceSupplier<?> instanceSupplier) {
|
||||
factoryMethod = instanceSupplier.getFactoryMethod();
|
||||
}
|
||||
return factoryMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.springframework.beans.factory.support;
|
|||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.beans.BeanInstantiationException;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
@ -27,7 +28,6 @@ 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.
|
||||
|
@ -60,7 +60,7 @@ 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 in favor of {@link #instantiateWithFactoryMethod(Method, Supplier)}
|
||||
*/
|
||||
@Deprecated(since = "6.2", forRemoval = true)
|
||||
public static void setCurrentlyInvokedFactoryMethod(@Nullable Method method) {
|
||||
|
@ -81,7 +81,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
|
|||
* @return the result of the instance supplier
|
||||
* @since 6.2
|
||||
*/
|
||||
public static <T> T instantiateWithFactoryMethod(Method method, ThrowingSupplier<T> instanceSupplier) {
|
||||
public static <T> T instantiateWithFactoryMethod(Method method, Supplier<T> instanceSupplier) {
|
||||
Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
|
||||
try {
|
||||
currentlyInvokedFactoryMethod.set(method);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -122,7 +122,7 @@ class InstanceSupplierCodeGeneratorTests {
|
|||
NoDependencyComponent bean = getBean(beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(NoDependencyComponent.class);
|
||||
assertThat(compiled.getSourceFile()).contains(
|
||||
"getBeanFactory().getBean(InnerComponentConfiguration.class).new NoDependencyComponent()");
|
||||
"InstanceSupplier.using(InnerComponentConfiguration.NoDependencyComponent::new");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(NoDependencyComponent.class))
|
||||
.satisfies(hasConstructorWithMode(ExecutableMode.INTROSPECT));
|
||||
|
@ -137,7 +137,7 @@ class InstanceSupplierCodeGeneratorTests {
|
|||
EnvironmentAwareComponent bean = getBean(beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(EnvironmentAwareComponent.class);
|
||||
assertThat(compiled.getSourceFile()).contains(
|
||||
"getBeanFactory().getBean(InnerComponentConfiguration.class).new EnvironmentAwareComponent(");
|
||||
"new InnerComponentConfiguration.EnvironmentAwareComponent(");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(EnvironmentAwareComponent.class))
|
||||
.satisfies(hasConstructorWithMode(ExecutableMode.INTROSPECT));
|
||||
|
@ -182,7 +182,7 @@ class InstanceSupplierCodeGeneratorTests {
|
|||
assertThat(bean).isInstanceOf(String.class);
|
||||
assertThat(bean).isEqualTo("Hello");
|
||||
assertThat(compiled.getSourceFile()).contains(
|
||||
"getBeanFactory().getBean(SimpleConfiguration.class).stringBean()");
|
||||
"getBeanFactory().getBean(\"config\", SimpleConfiguration.class).stringBean()");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(SimpleConfiguration.class))
|
||||
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));
|
||||
|
@ -199,7 +199,7 @@ class InstanceSupplierCodeGeneratorTests {
|
|||
Object bean = getBean(beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(SimpleBean.class);
|
||||
assertThat(compiled.getSourceFile()).contains(
|
||||
"getBeanFactory().getBean(DefaultSimpleBeanContract.class).simpleBean()");
|
||||
"getBeanFactory().getBean(\"config\", DefaultSimpleBeanContract.class).simpleBean()");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(SimpleBeanContract.class))
|
||||
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));
|
||||
|
@ -228,10 +228,8 @@ class InstanceSupplierCodeGeneratorTests {
|
|||
@Test
|
||||
void generateWhenHasStaticFactoryMethodWithNoArg() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(Integer.class)
|
||||
.setFactoryMethodOnBean("integerBean", "config").getBeanDefinition();
|
||||
this.beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(SimpleConfiguration.class).getBeanDefinition());
|
||||
.rootBeanDefinition(SimpleConfiguration.class)
|
||||
.setFactoryMethod("integerBean").getBeanDefinition();
|
||||
compile(beanDefinition, (instanceSupplier, compiled) -> {
|
||||
Integer bean = getBean(beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(Integer.class);
|
||||
|
@ -246,12 +244,10 @@ class InstanceSupplierCodeGeneratorTests {
|
|||
@Test
|
||||
void generateWhenHasStaticFactoryMethodWithArg() {
|
||||
RootBeanDefinition beanDefinition = (RootBeanDefinition) BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("create", "config").getBeanDefinition();
|
||||
.rootBeanDefinition(SimpleConfiguration.class)
|
||||
.setFactoryMethod("create").getBeanDefinition();
|
||||
beanDefinition.setResolvedFactoryMethod(ReflectionUtils
|
||||
.findMethod(SampleFactory.class, "create", Number.class, String.class));
|
||||
this.beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(SampleFactory.class).getBeanDefinition());
|
||||
this.beanFactory.registerSingleton("number", 42);
|
||||
this.beanFactory.registerSingleton("string", "test");
|
||||
compile(beanDefinition, (instanceSupplier, compiled) -> {
|
||||
|
@ -265,7 +261,7 @@ class InstanceSupplierCodeGeneratorTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void generateWhenHasStaticFactoryMethodCheckedException() {
|
||||
void generateWhenHasFactoryMethodCheckedException() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(Integer.class)
|
||||
.setFactoryMethodOnBean("throwingIntegerBean", "config")
|
||||
|
@ -282,107 +278,6 @@ class InstanceSupplierCodeGeneratorTests {
|
|||
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));
|
||||
}
|
||||
|
||||
@Nested
|
||||
@SuppressWarnings("deprecation")
|
||||
class DeprecationTests {
|
||||
|
||||
private static final TestCompiler TEST_COMPILER = TestCompiler.forSystem()
|
||||
.withCompilerOptions("-Xlint:all", "-Xlint:-rawtypes", "-Werror");
|
||||
|
||||
@Test
|
||||
@Disabled("Need to move to a separate method so that the warning can be suppressed")
|
||||
void generateWhenTargetClassIsDeprecated() {
|
||||
compileAndCheckWarnings(new RootBeanDefinition(DeprecatedBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetConstructorIsDeprecated() {
|
||||
compileAndCheckWarnings(new RootBeanDefinition(DeprecatedConstructor.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodIsDeprecated() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("deprecatedString", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedMemberConfiguration.class).getBeanDefinition());
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodParameterIsDeprecated() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("deprecatedParameter", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedMemberConfiguration.class).getBeanDefinition());
|
||||
beanFactory.registerBeanDefinition("parameter", new RootBeanDefinition(DeprecatedBean.class));
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodReturnTypeIsDeprecated() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(DeprecatedBean.class)
|
||||
.setFactoryMethodOnBean("deprecatedReturnType", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedMemberConfiguration.class).getBeanDefinition());
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
private void compileAndCheckWarnings(BeanDefinition beanDefinition) {
|
||||
assertThatNoException().isThrownBy(() -> compile(TEST_COMPILER, beanDefinition,
|
||||
((instanceSupplier, compiled) -> {})));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@SuppressWarnings("removal")
|
||||
class DeprecationForRemovalTests {
|
||||
|
||||
private static final TestCompiler TEST_COMPILER = TestCompiler.forSystem()
|
||||
.withCompilerOptions("-Xlint:all", "-Xlint:-rawtypes", "-Werror");
|
||||
|
||||
@Test
|
||||
@Disabled("Need to move to a separate method so that the warning can be suppressed")
|
||||
void generateWhenTargetClassIsDeprecatedForRemoval() {
|
||||
compileAndCheckWarnings(new RootBeanDefinition(DeprecatedForRemovalBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetConstructorIsDeprecatedForRemoval() {
|
||||
compileAndCheckWarnings(new RootBeanDefinition(DeprecatedForRemovalConstructor.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodIsDeprecatedForRemoval() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("deprecatedString", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedForRemovalMemberConfiguration.class).getBeanDefinition());
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodParameterIsDeprecatedForRemoval() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("deprecatedParameter", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedForRemovalMemberConfiguration.class).getBeanDefinition());
|
||||
beanFactory.registerBeanDefinition("parameter", new RootBeanDefinition(DeprecatedForRemovalBean.class));
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
private void compileAndCheckWarnings(BeanDefinition beanDefinition) {
|
||||
assertThatNoException().isThrownBy(() -> compile(TEST_COMPILER, beanDefinition,
|
||||
((instanceSupplier, compiled) -> {})));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private ReflectionHints getReflectionHints() {
|
||||
return this.generationContext.getRuntimeHints().reflection();
|
||||
|
@ -438,4 +333,106 @@ class InstanceSupplierCodeGeneratorTests {
|
|||
(InstanceSupplier<?>) compiled.getInstance(Supplier.class).get(), compiled));
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
@SuppressWarnings("deprecation")
|
||||
class DeprecationTests {
|
||||
|
||||
private static final TestCompiler TEST_COMPILER = TestCompiler.forSystem()
|
||||
.withCompilerOptions("-Xlint:all", "-Xlint:-rawtypes", "-Werror");
|
||||
|
||||
@Test
|
||||
@Disabled("Need to move to a separate method so that the warning can be suppressed")
|
||||
void generateWhenTargetClassIsDeprecated() {
|
||||
compileAndCheckWarnings(new RootBeanDefinition(DeprecatedBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetConstructorIsDeprecated() {
|
||||
compileAndCheckWarnings(new RootBeanDefinition(DeprecatedConstructor.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodIsDeprecated() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("deprecatedString", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedMemberConfiguration.class).getBeanDefinition());
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodParameterIsDeprecated() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("deprecatedParameter", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedMemberConfiguration.class).getBeanDefinition());
|
||||
beanFactory.registerBeanDefinition("parameter", new RootBeanDefinition(DeprecatedBean.class));
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodReturnTypeIsDeprecated() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(DeprecatedBean.class)
|
||||
.setFactoryMethodOnBean("deprecatedReturnType", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedMemberConfiguration.class).getBeanDefinition());
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
private void compileAndCheckWarnings(BeanDefinition beanDefinition) {
|
||||
assertThatNoException().isThrownBy(() -> compile(TEST_COMPILER, beanDefinition,
|
||||
((instanceSupplier, compiled) -> {})));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
@SuppressWarnings("removal")
|
||||
class DeprecationForRemovalTests {
|
||||
|
||||
private static final TestCompiler TEST_COMPILER = TestCompiler.forSystem()
|
||||
.withCompilerOptions("-Xlint:all", "-Xlint:-rawtypes", "-Werror");
|
||||
|
||||
@Test
|
||||
@Disabled("Need to move to a separate method so that the warning can be suppressed")
|
||||
void generateWhenTargetClassIsDeprecatedForRemoval() {
|
||||
compileAndCheckWarnings(new RootBeanDefinition(DeprecatedForRemovalBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetConstructorIsDeprecatedForRemoval() {
|
||||
compileAndCheckWarnings(new RootBeanDefinition(DeprecatedForRemovalConstructor.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodIsDeprecatedForRemoval() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("deprecatedString", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedForRemovalMemberConfiguration.class).getBeanDefinition());
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodParameterIsDeprecatedForRemoval() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("deprecatedParameter", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedForRemovalMemberConfiguration.class).getBeanDefinition());
|
||||
beanFactory.registerBeanDefinition("parameter", new RootBeanDefinition(DeprecatedForRemovalBean.class));
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
private void compileAndCheckWarnings(BeanDefinition beanDefinition) {
|
||||
assertThatNoException().isThrownBy(() -> compile(TEST_COMPILER, beanDefinition,
|
||||
((instanceSupplier, compiled) -> {})));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -26,6 +26,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link RootBeanDefinition}.
|
||||
|
@ -54,7 +55,7 @@ class RootBeanDefinitionTests {
|
|||
beanDefinition.setResolvedFactoryMethod(method);
|
||||
beanDefinition.setInstanceSupplier(instanceSupplier);
|
||||
assertThat(beanDefinition.getResolvedFactoryMethod()).isEqualTo(method);
|
||||
verify(instanceSupplier).getFactoryMethod();
|
||||
verifyNoInteractions(instanceSupplier);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -81,15 +82,15 @@ class RootBeanDefinitionTests {
|
|||
assertThat(beanDefinition.getDestroyMethodNames()).isNull();
|
||||
}
|
||||
|
||||
|
||||
static class BeanWithCloseMethod {
|
||||
|
||||
public void close() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class BeanWithNoDestroyMethod {
|
||||
|
||||
static class BeanWithNoDestroyMethod {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ class InstanceSupplierCodeGeneratorKotlinTests {
|
|||
Assertions.assertThat(bean).isInstanceOf(String::class.java)
|
||||
Assertions.assertThat(bean).isEqualTo("Hello")
|
||||
Assertions.assertThat(compiled.sourceFile).contains(
|
||||
"getBeanFactory().getBean(KotlinConfiguration.class).stringBean()"
|
||||
"getBeanFactory().getBean(\"config\", KotlinConfiguration.class).stringBean()"
|
||||
)
|
||||
}
|
||||
Assertions.assertThat<TypeHint?>(getReflectionHints().getTypeHint(KotlinConfiguration::class.java))
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -20,17 +20,16 @@ import org.springframework.core.env.Environment;
|
|||
|
||||
public class InnerComponentConfiguration {
|
||||
|
||||
public class NoDependencyComponent {
|
||||
public static class NoDependencyComponent {
|
||||
|
||||
public NoDependencyComponent() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class EnvironmentAwareComponent {
|
||||
public static class EnvironmentAwareComponent {
|
||||
|
||||
public EnvironmentAwareComponent(Environment environment) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -20,9 +20,6 @@ import java.io.IOException;
|
|||
|
||||
public class SimpleConfiguration {
|
||||
|
||||
public SimpleConfiguration() {
|
||||
}
|
||||
|
||||
public String stringBean() {
|
||||
return "Hello";
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue