Add generics to BeanInstanceSupplier
Update `BeanInstanceSupplier` to support a generic type. See gh-28748
This commit is contained in:
@@ -79,21 +79,22 @@ import org.springframework.util.function.ThrowingSupplier;
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0
|
||||
* @param <T> the type of instance supplied by this supplier
|
||||
* @see AutowiredArguments
|
||||
*/
|
||||
public final class BeanInstanceSupplier extends AutowiredElementResolver implements InstanceSupplier<Object> {
|
||||
public final class BeanInstanceSupplier<T> extends AutowiredElementResolver implements InstanceSupplier<T> {
|
||||
|
||||
private final ExecutableLookup lookup;
|
||||
|
||||
@Nullable
|
||||
private final ThrowingBiFunction<RegisteredBean, AutowiredArguments, Object> generator;
|
||||
private final ThrowingBiFunction<RegisteredBean, AutowiredArguments, T> generator;
|
||||
|
||||
@Nullable
|
||||
private final String[] shortcuts;
|
||||
|
||||
|
||||
private BeanInstanceSupplier(ExecutableLookup lookup,
|
||||
@Nullable ThrowingBiFunction<RegisteredBean, AutowiredArguments, Object> generator,
|
||||
@Nullable ThrowingBiFunction<RegisteredBean, AutowiredArguments, T> generator,
|
||||
@Nullable String[] shortcuts) {
|
||||
this.lookup = lookup;
|
||||
this.generator = generator;
|
||||
@@ -103,28 +104,30 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impleme
|
||||
/**
|
||||
* Create a {@link BeanInstanceSupplier} that resolves
|
||||
* arguments for the specified bean constructor.
|
||||
* @param <T> the type of instance supplied
|
||||
* @param parameterTypes the constructor parameter types
|
||||
* @return a new {@link BeanInstanceSupplier} instance
|
||||
*/
|
||||
public static BeanInstanceSupplier forConstructor(
|
||||
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(
|
||||
return new BeanInstanceSupplier<>(
|
||||
new ConstructorLookup(parameterTypes), null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link BeanInstanceSupplier} that
|
||||
* resolves arguments for the specified factory method.
|
||||
* @param <T> the type of instance supplied
|
||||
* @param declaringClass the class that declares the factory method
|
||||
* @param methodName the factory method name
|
||||
* @param parameterTypes the factory method parameter types
|
||||
* @return a new {@link BeanInstanceSupplier} instance
|
||||
*/
|
||||
public static BeanInstanceSupplier forFactoryMethod(
|
||||
public static <T> BeanInstanceSupplier<T> forFactoryMethod(
|
||||
Class<?> declaringClass, String methodName, Class<?>... parameterTypes) {
|
||||
|
||||
Assert.notNull(declaringClass, "'declaringClass' must not be null");
|
||||
@@ -132,7 +135,7 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impleme
|
||||
Assert.notNull(parameterTypes, "'parameterTypes' must not be null");
|
||||
Assert.noNullElements(parameterTypes,
|
||||
"'parameterTypes' must not contain null elements");
|
||||
return new BeanInstanceSupplier(
|
||||
return new BeanInstanceSupplier<>(
|
||||
new FactoryMethodLookup(declaringClass, methodName, parameterTypes),
|
||||
null, null);
|
||||
}
|
||||
@@ -151,10 +154,10 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impleme
|
||||
* @return a new {@link BeanInstanceSupplier} instance with the specified
|
||||
* generator
|
||||
*/
|
||||
public BeanInstanceSupplier withGenerator(
|
||||
ThrowingBiFunction<RegisteredBean, AutowiredArguments, Object> 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<T>(this.lookup, generator, this.shortcuts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,10 +168,10 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impleme
|
||||
* @return a new {@link BeanInstanceSupplier} instance with the specified
|
||||
* generator
|
||||
*/
|
||||
public BeanInstanceSupplier withGenerator(
|
||||
ThrowingFunction<RegisteredBean, Object> generator) {
|
||||
public BeanInstanceSupplier<T> withGenerator(
|
||||
ThrowingFunction<RegisteredBean, T> generator) {
|
||||
Assert.notNull(generator, "'generator' must not be null");
|
||||
return new BeanInstanceSupplier(this.lookup, (registeredBean, args) ->
|
||||
return new BeanInstanceSupplier<>(this.lookup, (registeredBean, args) ->
|
||||
generator.apply(registeredBean), this.shortcuts);
|
||||
}
|
||||
|
||||
@@ -180,9 +183,9 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impleme
|
||||
* @return a new {@link BeanInstanceSupplier} instance with the specified
|
||||
* generator
|
||||
*/
|
||||
public BeanInstanceSupplier withGenerator(ThrowingSupplier<Object> generator) {
|
||||
public BeanInstanceSupplier<T> withGenerator(ThrowingSupplier<T> generator) {
|
||||
Assert.notNull(generator, "'generator' must not be null");
|
||||
return new BeanInstanceSupplier(this.lookup, (registeredBean, args) ->
|
||||
return new BeanInstanceSupplier<>(this.lookup, (registeredBean, args) ->
|
||||
generator.get(), this.shortcuts);
|
||||
}
|
||||
|
||||
@@ -194,22 +197,19 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impleme
|
||||
* @return a new {@link BeanInstanceSupplier} instance
|
||||
* that uses the shortcuts
|
||||
*/
|
||||
public BeanInstanceSupplier withShortcuts(String... beanNames) {
|
||||
return new BeanInstanceSupplier(this.lookup, this.generator, beanNames);
|
||||
public BeanInstanceSupplier<T> withShortcuts(String... beanNames) {
|
||||
return new BeanInstanceSupplier<T>(this.lookup, this.generator, beanNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(RegisteredBean registeredBean) throws Exception {
|
||||
public T get(RegisteredBean registeredBean) throws Exception {
|
||||
Assert.notNull(registeredBean, "'registeredBean' must not be null");
|
||||
Executable executable = this.lookup.get(registeredBean);
|
||||
AutowiredArguments arguments = resolveArguments(registeredBean, executable);
|
||||
if (this.generator != null) {
|
||||
return this.generator.apply(registeredBean, arguments);
|
||||
}
|
||||
else {
|
||||
return instantiate(registeredBean.getBeanFactory(), executable,
|
||||
arguments.toArray());
|
||||
}
|
||||
return instantiate(registeredBean.getBeanFactory(), executable, arguments.toArray());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -351,15 +351,16 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impleme
|
||||
}
|
||||
}
|
||||
|
||||
private Object instantiate(ConfigurableBeanFactory beanFactory, Executable executable,
|
||||
@SuppressWarnings("unchecked")
|
||||
private T instantiate(ConfigurableBeanFactory beanFactory, Executable executable,
|
||||
Object[] arguments) {
|
||||
|
||||
try {
|
||||
if (executable instanceof Constructor<?> constructor) {
|
||||
return instantiate(constructor, arguments);
|
||||
return (T) instantiate(constructor, arguments);
|
||||
}
|
||||
if (executable instanceof Method method) {
|
||||
return instantiate(beanFactory, method, arguments);
|
||||
return (T) instantiate(beanFactory, method, arguments);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
||||
@@ -41,10 +41,8 @@ import org.springframework.javapoet.MethodSpec;
|
||||
class BeanRegistrationsAotContribution
|
||||
implements BeanFactoryInitializationAotContribution {
|
||||
|
||||
|
||||
private static final String BEAN_FACTORY_PARAMETER_NAME = "beanFactory";
|
||||
|
||||
|
||||
private final Map<String, BeanDefinitionMethodGenerator> registrations;
|
||||
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.javapoet.ClassName;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.CodeBlock.Builder;
|
||||
import org.springframework.javapoet.MethodSpec;
|
||||
import org.springframework.javapoet.ParameterizedTypeName;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.function.ThrowingSupplier;
|
||||
|
||||
@@ -108,7 +109,8 @@ class InstanceSupplierCodeGenerator {
|
||||
private CodeBlock generateCodeForConstructor(RegisteredBean registeredBean,
|
||||
Constructor<?> constructor) {
|
||||
|
||||
String name = registeredBean.getBeanName();
|
||||
String beanName = registeredBean.getBeanName();
|
||||
Class<?> beanClass = registeredBean.getBeanClass();
|
||||
Class<?> declaringClass = ClassUtils
|
||||
.getUserClass(constructor.getDeclaringClass());
|
||||
boolean dependsOnBean = ClassUtils.isInnerClass(declaringClass);
|
||||
@@ -116,13 +118,13 @@ class InstanceSupplierCodeGenerator {
|
||||
constructor);
|
||||
if (accessVisibility == AccessVisibility.PUBLIC
|
||||
|| accessVisibility == AccessVisibility.PACKAGE_PRIVATE) {
|
||||
return generateCodeForAccessibleConstructor(name, constructor, dependsOnBean,
|
||||
declaringClass);
|
||||
return generateCodeForAccessibleConstructor(beanName, beanClass, constructor,
|
||||
dependsOnBean, declaringClass);
|
||||
}
|
||||
return generateCodeForInaccessibleConstructor(name, constructor, dependsOnBean);
|
||||
return generateCodeForInaccessibleConstructor(beanName, beanClass, constructor, dependsOnBean);
|
||||
}
|
||||
|
||||
private CodeBlock generateCodeForAccessibleConstructor(String name,
|
||||
private CodeBlock generateCodeForAccessibleConstructor(String beanName, Class<?> beanClass,
|
||||
Constructor<?> constructor, boolean dependsOnBean, Class<?> declaringClass) {
|
||||
|
||||
this.generationContext.getRuntimeHints().reflection()
|
||||
@@ -139,37 +141,36 @@ class InstanceSupplierCodeGenerator {
|
||||
declaringClass);
|
||||
}
|
||||
GeneratedMethod generatedMethod = generateGetInstanceSupplierMethod(method ->
|
||||
buildGetInstanceMethodForConstructor(method, name, constructor,
|
||||
buildGetInstanceMethodForConstructor(method, beanName, beanClass, constructor,
|
||||
declaringClass, dependsOnBean, PRIVATE_STATIC));
|
||||
return generateReturnStatement(generatedMethod);
|
||||
}
|
||||
|
||||
private CodeBlock generateCodeForInaccessibleConstructor(String name,
|
||||
Constructor<?> constructor, boolean dependsOnBean) {
|
||||
private CodeBlock generateCodeForInaccessibleConstructor(String beanName,
|
||||
Class<?> beanClass, Constructor<?> constructor, boolean dependsOnBean) {
|
||||
|
||||
this.generationContext.getRuntimeHints().reflection()
|
||||
.registerConstructor(constructor);
|
||||
GeneratedMethod generatedMethod = generateGetInstanceSupplierMethod(method -> {
|
||||
method.addJavadoc("Get the bean instance supplier for '$L'.", name);
|
||||
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
||||
method.addModifiers(PRIVATE_STATIC);
|
||||
method.returns(BeanInstanceSupplier.class);
|
||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
|
||||
int parameterOffset = (!dependsOnBean) ? 0 : 1;
|
||||
method.addStatement(
|
||||
generateResolverForConstructor(constructor, parameterOffset));
|
||||
method.addStatement(generateResolverForConstructor(beanClass, constructor, parameterOffset));
|
||||
});
|
||||
return generateReturnStatement(generatedMethod);
|
||||
}
|
||||
|
||||
private void buildGetInstanceMethodForConstructor(MethodSpec.Builder method,
|
||||
String name, Constructor<?> constructor, Class<?> declaringClass,
|
||||
String beanName, Class<?> beanClass, Constructor<?> constructor, Class<?> declaringClass,
|
||||
boolean dependsOnBean, javax.lang.model.element.Modifier... modifiers) {
|
||||
|
||||
method.addJavadoc("Get the bean instance supplier for '$L'.", name);
|
||||
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
||||
method.addModifiers(modifiers);
|
||||
method.returns(BeanInstanceSupplier.class);
|
||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
|
||||
int parameterOffset = (!dependsOnBean) ? 0 : 1;
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
code.add(generateResolverForConstructor(constructor, parameterOffset));
|
||||
code.add(generateResolverForConstructor(beanClass, constructor, parameterOffset));
|
||||
boolean hasArguments = constructor.getParameterCount() > 0;
|
||||
CodeBlock arguments = hasArguments
|
||||
? new AutowiredArgumentsCodeGenerator(declaringClass, constructor)
|
||||
@@ -181,13 +182,13 @@ class InstanceSupplierCodeGenerator {
|
||||
method.addStatement(code.build());
|
||||
}
|
||||
|
||||
private CodeBlock generateResolverForConstructor(Constructor<?> constructor,
|
||||
int parameterOffset) {
|
||||
private CodeBlock generateResolverForConstructor(Class<?> beanClass,
|
||||
Constructor<?> constructor, int parameterOffset) {
|
||||
|
||||
CodeBlock parameterTypes = generateParameterTypesCode(
|
||||
constructor.getParameterTypes(), parameterOffset);
|
||||
return CodeBlock.of("return $T.forConstructor($L)",
|
||||
BeanInstanceSupplier.class, parameterTypes);
|
||||
return CodeBlock.of("return $T.<$T>forConstructor($L)",
|
||||
BeanInstanceSupplier.class, beanClass, parameterTypes);
|
||||
}
|
||||
|
||||
private CodeBlock generateNewInstanceCodeForConstructor(boolean dependsOnBean,
|
||||
@@ -204,7 +205,8 @@ class InstanceSupplierCodeGenerator {
|
||||
private CodeBlock generateCodeForFactoryMethod(RegisteredBean registeredBean,
|
||||
Method factoryMethod) {
|
||||
|
||||
String name = registeredBean.getBeanName();
|
||||
String beanName = registeredBean.getBeanName();
|
||||
Class<?> beanClass = registeredBean.getBeanClass();
|
||||
Class<?> declaringClass = ClassUtils
|
||||
.getUserClass(factoryMethod.getDeclaringClass());
|
||||
boolean dependsOnBean = !Modifier.isStatic(factoryMethod.getModifiers());
|
||||
@@ -212,55 +214,56 @@ class InstanceSupplierCodeGenerator {
|
||||
factoryMethod);
|
||||
if (accessVisibility == AccessVisibility.PUBLIC
|
||||
|| accessVisibility == AccessVisibility.PACKAGE_PRIVATE) {
|
||||
return generateCodeForAccessibleFactoryMethod(name, factoryMethod,
|
||||
return generateCodeForAccessibleFactoryMethod(beanName, beanClass, factoryMethod,
|
||||
declaringClass, dependsOnBean);
|
||||
}
|
||||
return generateCodeForInaccessibleFactoryMethod(name, factoryMethod,
|
||||
return generateCodeForInaccessibleFactoryMethod(beanName, beanClass, factoryMethod,
|
||||
declaringClass);
|
||||
}
|
||||
|
||||
private CodeBlock generateCodeForAccessibleFactoryMethod(String name,
|
||||
Method factoryMethod, Class<?> declaringClass, boolean dependsOnBean) {
|
||||
private CodeBlock generateCodeForAccessibleFactoryMethod(String beanName,
|
||||
Class<?> beanClass, Method factoryMethod, Class<?> declaringClass, boolean dependsOnBean) {
|
||||
|
||||
this.generationContext.getRuntimeHints().reflection()
|
||||
.registerMethod(factoryMethod, INTROSPECT);
|
||||
if (!dependsOnBean && factoryMethod.getParameterCount() == 0) {
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
code.add("$T.forFactoryMethod($T.class, $S)", BeanInstanceSupplier.class,
|
||||
declaringClass, factoryMethod.getName());
|
||||
code.add("$T.<$T>forFactoryMethod($T.class, $S)", BeanInstanceSupplier.class,
|
||||
beanClass, declaringClass, factoryMethod.getName());
|
||||
code.add(".withGenerator($T::$L)", declaringClass, factoryMethod.getName());
|
||||
return code.build();
|
||||
}
|
||||
GeneratedMethod getInstanceMethod = generateGetInstanceSupplierMethod(method ->
|
||||
buildGetInstanceMethodForFactoryMethod(method, name, factoryMethod,
|
||||
buildGetInstanceMethodForFactoryMethod(method, beanName, beanClass, factoryMethod,
|
||||
declaringClass, dependsOnBean, PRIVATE_STATIC));
|
||||
return generateReturnStatement(getInstanceMethod);
|
||||
}
|
||||
|
||||
private CodeBlock generateCodeForInaccessibleFactoryMethod(String name,
|
||||
private CodeBlock generateCodeForInaccessibleFactoryMethod(String beanName, Class<?> beanClass,
|
||||
Method factoryMethod, Class<?> declaringClass) {
|
||||
|
||||
this.generationContext.getRuntimeHints().reflection()
|
||||
.registerMethod(factoryMethod);
|
||||
GeneratedMethod getInstanceMethod = generateGetInstanceSupplierMethod(method -> {
|
||||
method.addJavadoc("Get the bean instance supplier for '$L'.", name);
|
||||
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
||||
method.addModifiers(PRIVATE_STATIC);
|
||||
method.returns(BeanInstanceSupplier.class);
|
||||
method.addStatement(generateInstanceSupplierForFactoryMethod(factoryMethod,
|
||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
|
||||
method.addStatement(generateInstanceSupplierForFactoryMethod(beanClass, factoryMethod,
|
||||
declaringClass, factoryMethod.getName()));
|
||||
});
|
||||
return generateReturnStatement(getInstanceMethod);
|
||||
}
|
||||
|
||||
private void buildGetInstanceMethodForFactoryMethod(MethodSpec.Builder method,
|
||||
String name, Method factoryMethod, Class<?> declaringClass,
|
||||
String beanName, Class<?> beanClass, Method factoryMethod, Class<?> declaringClass,
|
||||
boolean dependsOnBean, javax.lang.model.element.Modifier... modifiers) {
|
||||
|
||||
String factoryMethodName = factoryMethod.getName();
|
||||
method.addJavadoc("Get the bean instance supplier for '$L'.", name);
|
||||
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
||||
method.addModifiers(modifiers);
|
||||
method.returns(BeanInstanceSupplier.class);
|
||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
code.add(generateInstanceSupplierForFactoryMethod(factoryMethod, declaringClass, factoryMethodName));
|
||||
code.add(generateInstanceSupplierForFactoryMethod(beanClass, factoryMethod, declaringClass, factoryMethodName));
|
||||
boolean hasArguments = factoryMethod.getParameterCount() > 0;
|
||||
CodeBlock arguments = hasArguments
|
||||
? new AutowiredArgumentsCodeGenerator(declaringClass, factoryMethod)
|
||||
@@ -272,18 +275,18 @@ class InstanceSupplierCodeGenerator {
|
||||
method.addStatement(code.build());
|
||||
}
|
||||
|
||||
private CodeBlock generateInstanceSupplierForFactoryMethod(Method factoryMethod,
|
||||
Class<?> declaringClass, String factoryMethodName) {
|
||||
private CodeBlock generateInstanceSupplierForFactoryMethod(Class<?> beanClass,
|
||||
Method factoryMethod, Class<?> declaringClass, String factoryMethodName) {
|
||||
|
||||
if (factoryMethod.getParameterCount() == 0) {
|
||||
return CodeBlock.of("return $T.forFactoryMethod($T.class, $S)",
|
||||
BeanInstanceSupplier.class, declaringClass,
|
||||
return CodeBlock.of("return $T.<$T>forFactoryMethod($T.class, $S)",
|
||||
BeanInstanceSupplier.class, beanClass, declaringClass,
|
||||
factoryMethodName);
|
||||
}
|
||||
CodeBlock parameterTypes = generateParameterTypesCode(
|
||||
factoryMethod.getParameterTypes(), 0);
|
||||
return CodeBlock.of("return $T.forFactoryMethod($T.class, $S, $L)",
|
||||
BeanInstanceSupplier.class, declaringClass,
|
||||
return CodeBlock.of("return $T.<$T>forFactoryMethod($T.class, $S, $L)",
|
||||
BeanInstanceSupplier.class, beanClass, declaringClass,
|
||||
factoryMethodName, parameterTypes);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user