From 4c7b04e32a0b17eb8e2d670a6a6319012770d535 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 1 Dec 2022 17:46:23 +0100 Subject: [PATCH] Use resolved factory method return type for supplier code generation Closes gh-29598 --- .../aot/InstanceSupplierCodeGenerator.java | 73 +++++++++++-------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java index ee140ce108e4..7f8104531d6e 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java @@ -46,16 +46,16 @@ * Internal code generator to create an {@link InstanceSupplier}, usually in * the form of a {@link BeanInstanceSupplier} that retains the executable * that is used to instantiate the bean. - *

- * Generated code is usually a method reference that generate the - * {@link BeanInstanceSupplier}, but some shortcut can be used as well such - * as: + * + *

Generated code is usually a method reference that generate the + * {@link BeanInstanceSupplier}, but some shortcut can be used as well such as: *

  * {@code InstanceSupplier.of(TheGeneratedClass::getMyBeanInstance);}
  * 
* * @author Phillip Webb * @author Stephane Nicoll + * @author Juergen Hoeller * @since 6.0 */ class InstanceSupplierCodeGenerator { @@ -90,9 +90,7 @@ class InstanceSupplierCodeGenerator { } - CodeBlock generateCode(RegisteredBean registeredBean, - Executable constructorOrFactoryMethod) { - + CodeBlock generateCode(RegisteredBean registeredBean, Executable constructorOrFactoryMethod) { if (constructorOrFactoryMethod instanceof Constructor constructor) { return generateCodeForConstructor(registeredBean, constructor); } @@ -108,6 +106,7 @@ private CodeBlock generateCodeForConstructor(RegisteredBean registeredBean, Cons Class beanClass = registeredBean.getBeanClass(); Class declaringClass = constructor.getDeclaringClass(); boolean dependsOnBean = ClassUtils.isInnerClass(declaringClass); + Visibility accessVisibility = getAccessVisibility(registeredBean, constructor); if (accessVisibility != Visibility.PRIVATE) { return generateCodeForAccessibleConstructor(beanName, beanClass, constructor, @@ -121,6 +120,7 @@ private CodeBlock generateCodeForAccessibleConstructor(String beanName, Class this.generationContext.getRuntimeHints().reflection().registerConstructor( constructor, ExecutableMode.INTROSPECT); + if (!dependsOnBean && constructor.getParameterCount() == 0) { if (!this.allowDirectSupplierShortcut) { return CodeBlock.of("$T.using($T::new)", InstanceSupplier.class, declaringClass); @@ -130,6 +130,7 @@ private CodeBlock generateCodeForAccessibleConstructor(String beanName, Class } return CodeBlock.of("$T.of($T::new)", ThrowingSupplier.class, declaringClass); } + GeneratedMethod generatedMethod = generateGetInstanceSupplierMethod(method -> buildGetInstanceMethodForConstructor(method, beanName, beanClass, constructor, declaringClass, dependsOnBean, PRIVATE_STATIC)); @@ -141,6 +142,7 @@ private CodeBlock generateCodeForInaccessibleConstructor(String beanName, this.generationContext.getRuntimeHints().reflection() .registerConstructor(constructor, ExecutableMode.INVOKE); + GeneratedMethod generatedMethod = generateGetInstanceSupplierMethod(method -> { method.addJavadoc("Get the bean instance supplier for '$L'.", beanName); method.addModifiers(PRIVATE_STATIC); @@ -148,6 +150,7 @@ private CodeBlock generateCodeForInaccessibleConstructor(String beanName, int parameterOffset = (!dependsOnBean) ? 0 : 1; method.addStatement(generateResolverForConstructor(beanClass, constructor, parameterOffset)); }); + return generateReturnStatement(generatedMethod); } @@ -158,14 +161,17 @@ private void buildGetInstanceMethodForConstructor(MethodSpec.Builder method, method.addJavadoc("Get the bean instance supplier for '$L'.", beanName); method.addModifiers(modifiers); method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass)); + int parameterOffset = (!dependsOnBean) ? 0 : 1; CodeBlock.Builder code = CodeBlock.builder(); code.add(generateResolverForConstructor(beanClass, constructor, parameterOffset)); boolean hasArguments = constructor.getParameterCount() > 0; + CodeBlock arguments = hasArguments ? new AutowiredArgumentsCodeGenerator(declaringClass, constructor) .generateCode(constructor.getParameterTypes(), parameterOffset) : NO_ARGS; + CodeBlock newInstance = generateNewInstanceCodeForConstructor(dependsOnBean, declaringClass, arguments); code.add(generateWithGeneratorCode(hasArguments, newInstance)); method.addStatement(code.build()); @@ -184,92 +190,99 @@ private CodeBlock generateNewInstanceCodeForConstructor(boolean dependsOnBean, 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 generateCodeForFactoryMethod(RegisteredBean registeredBean, - Method factoryMethod) { - + private CodeBlock generateCodeForFactoryMethod(RegisteredBean registeredBean, Method factoryMethod) { String beanName = registeredBean.getBeanName(); - Class beanClass = registeredBean.getBeanClass(); - Class declaringClass = ClassUtils - .getUserClass(factoryMethod.getDeclaringClass()); + Class declaringClass = ClassUtils.getUserClass(factoryMethod.getDeclaringClass()); boolean dependsOnBean = !Modifier.isStatic(factoryMethod.getModifiers()); + Visibility accessVisibility = getAccessVisibility(registeredBean, factoryMethod); if (accessVisibility != Visibility.PRIVATE) { return generateCodeForAccessibleFactoryMethod( - beanName, beanClass, factoryMethod, declaringClass, dependsOnBean); + beanName, factoryMethod, declaringClass, dependsOnBean); } - return generateCodeForInaccessibleFactoryMethod(beanName, beanClass, factoryMethod, declaringClass); + return generateCodeForInaccessibleFactoryMethod(beanName, factoryMethod, declaringClass); } private CodeBlock generateCodeForAccessibleFactoryMethod(String beanName, - Class beanClass, Method factoryMethod, Class declaringClass, boolean dependsOnBean) { + Method factoryMethod, Class declaringClass, boolean dependsOnBean) { this.generationContext.getRuntimeHints().reflection().registerMethod( factoryMethod, ExecutableMode.INTROSPECT); + if (!dependsOnBean && factoryMethod.getParameterCount() == 0) { + Class suppliedType = ClassUtils.resolvePrimitiveIfNecessary(factoryMethod.getReturnType()); CodeBlock.Builder code = CodeBlock.builder(); code.add("$T.<$T>forFactoryMethod($T.class, $S)", BeanInstanceSupplier.class, - beanClass, declaringClass, factoryMethod.getName()); + suppliedType, declaringClass, factoryMethod.getName()); code.add(".withGenerator($T::$L)", declaringClass, factoryMethod.getName()); return code.build(); } + GeneratedMethod getInstanceMethod = generateGetInstanceSupplierMethod(method -> - buildGetInstanceMethodForFactoryMethod(method, beanName, beanClass, factoryMethod, + buildGetInstanceMethodForFactoryMethod(method, beanName, factoryMethod, declaringClass, dependsOnBean, PRIVATE_STATIC)); return generateReturnStatement(getInstanceMethod); } - private CodeBlock generateCodeForInaccessibleFactoryMethod(String beanName, Class beanClass, - Method factoryMethod, Class declaringClass) { + private CodeBlock generateCodeForInaccessibleFactoryMethod( + String beanName, Method factoryMethod, Class declaringClass) { this.generationContext.getRuntimeHints().reflection().registerMethod(factoryMethod, ExecutableMode.INVOKE); GeneratedMethod getInstanceMethod = generateGetInstanceSupplierMethod(method -> { + Class suppliedType = ClassUtils.resolvePrimitiveIfNecessary(factoryMethod.getReturnType()); method.addJavadoc("Get the bean instance supplier for '$L'.", beanName); method.addModifiers(PRIVATE_STATIC); - method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass)); + method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, suppliedType)); method.addStatement(generateInstanceSupplierForFactoryMethod( - beanClass, factoryMethod, declaringClass, factoryMethod.getName())); + factoryMethod, suppliedType, declaringClass, factoryMethod.getName())); }); return generateReturnStatement(getInstanceMethod); } private void buildGetInstanceMethodForFactoryMethod(MethodSpec.Builder method, - String beanName, Class beanClass, Method factoryMethod, Class declaringClass, + String beanName, Method factoryMethod, Class declaringClass, boolean dependsOnBean, javax.lang.model.element.Modifier... modifiers) { String factoryMethodName = factoryMethod.getName(); + Class suppliedType = ClassUtils.resolvePrimitiveIfNecessary(factoryMethod.getReturnType()); + method.addJavadoc("Get the bean instance supplier for '$L'.", beanName); method.addModifiers(modifiers); - method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass)); + method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, suppliedType)); + CodeBlock.Builder code = CodeBlock.builder(); code.add(generateInstanceSupplierForFactoryMethod( - beanClass, factoryMethod, declaringClass, factoryMethodName)); + factoryMethod, suppliedType, declaringClass, factoryMethodName)); + boolean hasArguments = factoryMethod.getParameterCount() > 0; CodeBlock arguments = hasArguments ? new AutowiredArgumentsCodeGenerator(declaringClass, factoryMethod) .generateCode(factoryMethod.getParameterTypes()) : NO_ARGS; + CodeBlock newInstance = generateNewInstanceCodeForMethod( dependsOnBean, declaringClass, factoryMethodName, arguments); code.add(generateWithGeneratorCode(hasArguments, newInstance)); method.addStatement(code.build()); } - private CodeBlock generateInstanceSupplierForFactoryMethod(Class beanClass, - Method factoryMethod, Class declaringClass, String factoryMethodName) { + private CodeBlock generateInstanceSupplierForFactoryMethod(Method factoryMethod, + Class suppliedType, Class declaringClass, String factoryMethodName) { if (factoryMethod.getParameterCount() == 0) { return CodeBlock.of("return $T.<$T>forFactoryMethod($T.class, $S)", - BeanInstanceSupplier.class, beanClass, declaringClass, - factoryMethodName); + BeanInstanceSupplier.class, suppliedType, declaringClass, factoryMethodName); } + CodeBlock parameterTypes = generateParameterTypesCode(factoryMethod.getParameterTypes(), 0); return CodeBlock.of("return $T.<$T>forFactoryMethod($T.class, $S, $L)", - BeanInstanceSupplier.class, beanClass, declaringClass, factoryMethodName, parameterTypes); + BeanInstanceSupplier.class, suppliedType, declaringClass, factoryMethodName, parameterTypes); } private CodeBlock generateNewInstanceCodeForMethod(boolean dependsOnBean,