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:
Juergen Hoeller 2024-09-24 18:59:08 +02:00
parent aef5143642
commit d6e4bd7c90
12 changed files with 494 additions and 584 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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();

View File

@ -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;
}
/**

View File

@ -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);

View File

@ -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) -> {})));
}
}
}

View File

@ -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 {
}
}

View File

@ -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))

View File

@ -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) {
}
}
}

View File

@ -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";
}