Detect target of factory method with AOT
Previously, if a factory method is defined on a parent, the generated code would blindly use the method's declaring class for both the target of the generated code, and the signature of the method. This commit improves the resolution by considering the factory metadata in the BeanDefinition. Closes gh-32609
This commit is contained in:
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,7 +17,6 @@
|
|||||||
package org.springframework.beans.factory.aot;
|
package org.springframework.beans.factory.aot;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Executable;
|
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
@@ -35,6 +34,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
|||||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||||
import org.springframework.beans.factory.support.InstanceSupplier;
|
import org.springframework.beans.factory.support.InstanceSupplier;
|
||||||
import org.springframework.beans.factory.support.RegisteredBean;
|
import org.springframework.beans.factory.support.RegisteredBean;
|
||||||
|
import org.springframework.beans.factory.support.RegisteredBean.InstantiationDescriptor;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.javapoet.ClassName;
|
import org.springframework.javapoet.ClassName;
|
||||||
@@ -62,7 +62,7 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
|
|||||||
|
|
||||||
private final BeanDefinitionMethodGeneratorFactory beanDefinitionMethodGeneratorFactory;
|
private final BeanDefinitionMethodGeneratorFactory beanDefinitionMethodGeneratorFactory;
|
||||||
|
|
||||||
private final Supplier<Executable> constructorOrFactoryMethod;
|
private final Supplier<InstantiationDescriptor> instantiationDescriptor;
|
||||||
|
|
||||||
|
|
||||||
DefaultBeanRegistrationCodeFragments(BeanRegistrationsCode beanRegistrationsCode,
|
DefaultBeanRegistrationCodeFragments(BeanRegistrationsCode beanRegistrationsCode,
|
||||||
@@ -72,7 +72,7 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
|
|||||||
this.beanRegistrationsCode = beanRegistrationsCode;
|
this.beanRegistrationsCode = beanRegistrationsCode;
|
||||||
this.registeredBean = registeredBean;
|
this.registeredBean = registeredBean;
|
||||||
this.beanDefinitionMethodGeneratorFactory = beanDefinitionMethodGeneratorFactory;
|
this.beanDefinitionMethodGeneratorFactory = beanDefinitionMethodGeneratorFactory;
|
||||||
this.constructorOrFactoryMethod = SingletonSupplier.of(registeredBean::resolveConstructorOrFactoryMethod);
|
this.instantiationDescriptor = SingletonSupplier.of(registeredBean::resolveInstantiationDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
|
|||||||
throw new IllegalStateException("Default code generation is not supported for bean definitions "
|
throw new IllegalStateException("Default code generation is not supported for bean definitions "
|
||||||
+ "declaring an instance supplier callback: " + registeredBean.getMergedBeanDefinition());
|
+ "declaring an instance supplier callback: " + registeredBean.getMergedBeanDefinition());
|
||||||
}
|
}
|
||||||
Class<?> target = extractDeclaringClass(registeredBean.getBeanType(), this.constructorOrFactoryMethod.get());
|
Class<?> target = extractDeclaringClass(registeredBean, this.instantiationDescriptor.get());
|
||||||
while (target.getName().startsWith("java.") && registeredBean.isInnerBean()) {
|
while (target.getName().startsWith("java.") && registeredBean.isInnerBean()) {
|
||||||
RegisteredBean parent = registeredBean.getParent();
|
RegisteredBean parent = registeredBean.getParent();
|
||||||
Assert.state(parent != null, "No parent available for inner bean");
|
Assert.state(parent != null, "No parent available for inner bean");
|
||||||
@@ -91,14 +91,14 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
|
|||||||
return (target.isArray() ? ClassName.get(target.getComponentType()) : ClassName.get(target));
|
return (target.isArray() ? ClassName.get(target.getComponentType()) : ClassName.get(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Class<?> extractDeclaringClass(ResolvableType beanType, Executable executable) {
|
private Class<?> extractDeclaringClass(RegisteredBean registeredBean, InstantiationDescriptor instantiationDescriptor) {
|
||||||
Class<?> declaringClass = ClassUtils.getUserClass(executable.getDeclaringClass());
|
Class<?> declaringClass = ClassUtils.getUserClass(instantiationDescriptor.targetClass());
|
||||||
if (executable instanceof Constructor<?>
|
if (instantiationDescriptor.executable() instanceof Constructor<?>
|
||||||
&& AccessControl.forMember(executable).isPublic()
|
&& AccessControl.forMember(instantiationDescriptor.executable()).isPublic()
|
||||||
&& FactoryBean.class.isAssignableFrom(declaringClass)) {
|
&& FactoryBean.class.isAssignableFrom(declaringClass)) {
|
||||||
return extractTargetClassFromFactoryBean(declaringClass, beanType);
|
return extractTargetClassFromFactoryBean(declaringClass, registeredBean.getBeanType());
|
||||||
}
|
}
|
||||||
return executable.getDeclaringClass();
|
return declaringClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -238,9 +238,9 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
|
|||||||
throw new IllegalStateException("Default code generation is not supported for bean definitions declaring "
|
throw new IllegalStateException("Default code generation is not supported for bean definitions declaring "
|
||||||
+ "an instance supplier callback: " + this.registeredBean.getMergedBeanDefinition());
|
+ "an instance supplier callback: " + this.registeredBean.getMergedBeanDefinition());
|
||||||
}
|
}
|
||||||
return new InstanceSupplierCodeGenerator(generationContext,
|
return new InstanceSupplierCodeGenerator(generationContext, beanRegistrationCode.getClassName(),
|
||||||
beanRegistrationCode.getClassName(), beanRegistrationCode.getMethods(), allowDirectSupplierShortcut)
|
beanRegistrationCode.getMethods(), allowDirectSupplierShortcut).generateCode(
|
||||||
.generateCode(this.registeredBean, this.constructorOrFactoryMethod.get());
|
this.registeredBean, this.instantiationDescriptor.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -46,6 +46,7 @@ import org.springframework.beans.factory.support.AutowireCandidateResolver;
|
|||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.InstanceSupplier;
|
import org.springframework.beans.factory.support.InstanceSupplier;
|
||||||
import org.springframework.beans.factory.support.RegisteredBean;
|
import org.springframework.beans.factory.support.RegisteredBean;
|
||||||
|
import org.springframework.beans.factory.support.RegisteredBean.InstantiationDescriptor;
|
||||||
import org.springframework.core.KotlinDetector;
|
import org.springframework.core.KotlinDetector;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
@@ -120,14 +121,29 @@ public class InstanceSupplierCodeGenerator {
|
|||||||
* @param registeredBean the bean to handle
|
* @param registeredBean the bean to handle
|
||||||
* @param constructorOrFactoryMethod the executable to use to create the bean
|
* @param constructorOrFactoryMethod the executable to use to create the bean
|
||||||
* @return the generated code
|
* @return the generated code
|
||||||
|
* @deprecated in favor of {@link #generateCode(RegisteredBean, InstantiationDescriptor)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "6.1.7")
|
||||||
public CodeBlock generateCode(RegisteredBean registeredBean, Executable constructorOrFactoryMethod) {
|
public CodeBlock generateCode(RegisteredBean registeredBean, Executable constructorOrFactoryMethod) {
|
||||||
|
return generateCode(registeredBean, new InstantiationDescriptor(
|
||||||
|
constructorOrFactoryMethod, constructorOrFactoryMethod.getDeclaringClass()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the instance supplier code.
|
||||||
|
* @param registeredBean the bean to handle
|
||||||
|
* @param instantiationDescriptor the executable to use to create the bean
|
||||||
|
* @return the generated code
|
||||||
|
* @since 6.1.7
|
||||||
|
*/
|
||||||
|
public CodeBlock generateCode(RegisteredBean registeredBean, InstantiationDescriptor instantiationDescriptor) {
|
||||||
|
Executable constructorOrFactoryMethod = instantiationDescriptor.executable();
|
||||||
registerRuntimeHintsIfNecessary(registeredBean, constructorOrFactoryMethod);
|
registerRuntimeHintsIfNecessary(registeredBean, constructorOrFactoryMethod);
|
||||||
if (constructorOrFactoryMethod instanceof Constructor<?> constructor) {
|
if (constructorOrFactoryMethod instanceof Constructor<?> constructor) {
|
||||||
return generateCodeForConstructor(registeredBean, constructor);
|
return generateCodeForConstructor(registeredBean, constructor);
|
||||||
}
|
}
|
||||||
if (constructorOrFactoryMethod instanceof Method method) {
|
if (constructorOrFactoryMethod instanceof Method method) {
|
||||||
return generateCodeForFactoryMethod(registeredBean, method);
|
return generateCodeForFactoryMethod(registeredBean, method, instantiationDescriptor.targetClass());
|
||||||
}
|
}
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"No suitable executor found for " + registeredBean.getBeanName());
|
"No suitable executor found for " + registeredBean.getBeanName());
|
||||||
@@ -253,21 +269,21 @@ public class InstanceSupplierCodeGenerator {
|
|||||||
declaringClass.getSimpleName(), args);
|
declaringClass.getSimpleName(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CodeBlock generateCodeForFactoryMethod(RegisteredBean registeredBean, Method factoryMethod) {
|
private CodeBlock generateCodeForFactoryMethod(RegisteredBean registeredBean, Method factoryMethod, Class<?> targetClass) {
|
||||||
String beanName = registeredBean.getBeanName();
|
String beanName = registeredBean.getBeanName();
|
||||||
Class<?> declaringClass = ClassUtils.getUserClass(factoryMethod.getDeclaringClass());
|
Class<?> targetClassToUse = ClassUtils.getUserClass(targetClass);
|
||||||
boolean dependsOnBean = !Modifier.isStatic(factoryMethod.getModifiers());
|
boolean dependsOnBean = !Modifier.isStatic(factoryMethod.getModifiers());
|
||||||
|
|
||||||
Visibility accessVisibility = getAccessVisibility(registeredBean, factoryMethod);
|
Visibility accessVisibility = getAccessVisibility(registeredBean, factoryMethod);
|
||||||
if (accessVisibility != Visibility.PRIVATE) {
|
if (accessVisibility != Visibility.PRIVATE) {
|
||||||
return generateCodeForAccessibleFactoryMethod(
|
return generateCodeForAccessibleFactoryMethod(
|
||||||
beanName, factoryMethod, declaringClass, dependsOnBean);
|
beanName, factoryMethod, targetClassToUse, dependsOnBean);
|
||||||
}
|
}
|
||||||
return generateCodeForInaccessibleFactoryMethod(beanName, factoryMethod, declaringClass);
|
return generateCodeForInaccessibleFactoryMethod(beanName, factoryMethod, targetClassToUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CodeBlock generateCodeForAccessibleFactoryMethod(String beanName,
|
private CodeBlock generateCodeForAccessibleFactoryMethod(String beanName,
|
||||||
Method factoryMethod, Class<?> declaringClass, boolean dependsOnBean) {
|
Method factoryMethod, Class<?> targetClass, boolean dependsOnBean) {
|
||||||
|
|
||||||
this.generationContext.getRuntimeHints().reflection().registerMethod(
|
this.generationContext.getRuntimeHints().reflection().registerMethod(
|
||||||
factoryMethod, ExecutableMode.INTROSPECT);
|
factoryMethod, ExecutableMode.INTROSPECT);
|
||||||
@@ -276,20 +292,20 @@ public class InstanceSupplierCodeGenerator {
|
|||||||
Class<?> suppliedType = ClassUtils.resolvePrimitiveIfNecessary(factoryMethod.getReturnType());
|
Class<?> suppliedType = ClassUtils.resolvePrimitiveIfNecessary(factoryMethod.getReturnType());
|
||||||
CodeBlock.Builder code = CodeBlock.builder();
|
CodeBlock.Builder code = CodeBlock.builder();
|
||||||
code.add("$T.<$T>forFactoryMethod($T.class, $S)", BeanInstanceSupplier.class,
|
code.add("$T.<$T>forFactoryMethod($T.class, $S)", BeanInstanceSupplier.class,
|
||||||
suppliedType, declaringClass, factoryMethod.getName());
|
suppliedType, targetClass, factoryMethod.getName());
|
||||||
code.add(".withGenerator(($L) -> $T.$L())", REGISTERED_BEAN_PARAMETER_NAME,
|
code.add(".withGenerator(($L) -> $T.$L())", REGISTERED_BEAN_PARAMETER_NAME,
|
||||||
declaringClass, factoryMethod.getName());
|
targetClass, factoryMethod.getName());
|
||||||
return code.build();
|
return code.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
GeneratedMethod getInstanceMethod = generateGetInstanceSupplierMethod(method ->
|
GeneratedMethod getInstanceMethod = generateGetInstanceSupplierMethod(method ->
|
||||||
buildGetInstanceMethodForFactoryMethod(method, beanName, factoryMethod,
|
buildGetInstanceMethodForFactoryMethod(method, beanName, factoryMethod,
|
||||||
declaringClass, dependsOnBean, PRIVATE_STATIC));
|
targetClass, dependsOnBean, PRIVATE_STATIC));
|
||||||
return generateReturnStatement(getInstanceMethod);
|
return generateReturnStatement(getInstanceMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CodeBlock generateCodeForInaccessibleFactoryMethod(
|
private CodeBlock generateCodeForInaccessibleFactoryMethod(
|
||||||
String beanName, Method factoryMethod, Class<?> declaringClass) {
|
String beanName, Method factoryMethod, Class<?> targetClass) {
|
||||||
|
|
||||||
this.generationContext.getRuntimeHints().reflection().registerMethod(factoryMethod, ExecutableMode.INVOKE);
|
this.generationContext.getRuntimeHints().reflection().registerMethod(factoryMethod, ExecutableMode.INVOKE);
|
||||||
GeneratedMethod getInstanceMethod = generateGetInstanceSupplierMethod(method -> {
|
GeneratedMethod getInstanceMethod = generateGetInstanceSupplierMethod(method -> {
|
||||||
@@ -298,19 +314,19 @@ public class InstanceSupplierCodeGenerator {
|
|||||||
method.addModifiers(PRIVATE_STATIC);
|
method.addModifiers(PRIVATE_STATIC);
|
||||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, suppliedType));
|
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, suppliedType));
|
||||||
method.addStatement(generateInstanceSupplierForFactoryMethod(
|
method.addStatement(generateInstanceSupplierForFactoryMethod(
|
||||||
factoryMethod, suppliedType, declaringClass, factoryMethod.getName()));
|
factoryMethod, suppliedType, targetClass, factoryMethod.getName()));
|
||||||
});
|
});
|
||||||
return generateReturnStatement(getInstanceMethod);
|
return generateReturnStatement(getInstanceMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildGetInstanceMethodForFactoryMethod(MethodSpec.Builder method,
|
private void buildGetInstanceMethodForFactoryMethod(MethodSpec.Builder method,
|
||||||
String beanName, Method factoryMethod, Class<?> declaringClass,
|
String beanName, Method factoryMethod, Class<?> targetClass,
|
||||||
boolean dependsOnBean, javax.lang.model.element.Modifier... modifiers) {
|
boolean dependsOnBean, javax.lang.model.element.Modifier... modifiers) {
|
||||||
|
|
||||||
String factoryMethodName = factoryMethod.getName();
|
String factoryMethodName = factoryMethod.getName();
|
||||||
Class<?> suppliedType = ClassUtils.resolvePrimitiveIfNecessary(factoryMethod.getReturnType());
|
Class<?> suppliedType = ClassUtils.resolvePrimitiveIfNecessary(factoryMethod.getReturnType());
|
||||||
CodeWarnings codeWarnings = new CodeWarnings();
|
CodeWarnings codeWarnings = new CodeWarnings();
|
||||||
codeWarnings.detectDeprecation(declaringClass, factoryMethod, suppliedType)
|
codeWarnings.detectDeprecation(targetClass, factoryMethod, suppliedType)
|
||||||
.detectDeprecation(Arrays.stream(factoryMethod.getParameters()).map(Parameter::getType));
|
.detectDeprecation(Arrays.stream(factoryMethod.getParameters()).map(Parameter::getType));
|
||||||
|
|
||||||
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
||||||
@@ -320,41 +336,41 @@ public class InstanceSupplierCodeGenerator {
|
|||||||
|
|
||||||
CodeBlock.Builder code = CodeBlock.builder();
|
CodeBlock.Builder code = CodeBlock.builder();
|
||||||
code.add(generateInstanceSupplierForFactoryMethod(
|
code.add(generateInstanceSupplierForFactoryMethod(
|
||||||
factoryMethod, suppliedType, declaringClass, factoryMethodName));
|
factoryMethod, suppliedType, targetClass, factoryMethodName));
|
||||||
|
|
||||||
boolean hasArguments = factoryMethod.getParameterCount() > 0;
|
boolean hasArguments = factoryMethod.getParameterCount() > 0;
|
||||||
CodeBlock arguments = hasArguments ?
|
CodeBlock arguments = hasArguments ?
|
||||||
new AutowiredArgumentsCodeGenerator(declaringClass, factoryMethod)
|
new AutowiredArgumentsCodeGenerator(targetClass, factoryMethod)
|
||||||
.generateCode(factoryMethod.getParameterTypes())
|
.generateCode(factoryMethod.getParameterTypes())
|
||||||
: NO_ARGS;
|
: NO_ARGS;
|
||||||
|
|
||||||
CodeBlock newInstance = generateNewInstanceCodeForMethod(
|
CodeBlock newInstance = generateNewInstanceCodeForMethod(
|
||||||
dependsOnBean, declaringClass, factoryMethodName, arguments);
|
dependsOnBean, targetClass, factoryMethodName, arguments);
|
||||||
code.add(generateWithGeneratorCode(hasArguments, newInstance));
|
code.add(generateWithGeneratorCode(hasArguments, newInstance));
|
||||||
method.addStatement(code.build());
|
method.addStatement(code.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private CodeBlock generateInstanceSupplierForFactoryMethod(Method factoryMethod,
|
private CodeBlock generateInstanceSupplierForFactoryMethod(Method factoryMethod,
|
||||||
Class<?> suppliedType, Class<?> declaringClass, String factoryMethodName) {
|
Class<?> suppliedType, Class<?> targetClass, String factoryMethodName) {
|
||||||
|
|
||||||
if (factoryMethod.getParameterCount() == 0) {
|
if (factoryMethod.getParameterCount() == 0) {
|
||||||
return CodeBlock.of("return $T.<$T>forFactoryMethod($T.class, $S)",
|
return CodeBlock.of("return $T.<$T>forFactoryMethod($T.class, $S)",
|
||||||
BeanInstanceSupplier.class, suppliedType, declaringClass, factoryMethodName);
|
BeanInstanceSupplier.class, suppliedType, targetClass, factoryMethodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeBlock parameterTypes = generateParameterTypesCode(factoryMethod.getParameterTypes(), 0);
|
CodeBlock parameterTypes = generateParameterTypesCode(factoryMethod.getParameterTypes(), 0);
|
||||||
return CodeBlock.of("return $T.<$T>forFactoryMethod($T.class, $S, $L)",
|
return CodeBlock.of("return $T.<$T>forFactoryMethod($T.class, $S, $L)",
|
||||||
BeanInstanceSupplier.class, suppliedType, declaringClass, factoryMethodName, parameterTypes);
|
BeanInstanceSupplier.class, suppliedType, targetClass, factoryMethodName, parameterTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CodeBlock generateNewInstanceCodeForMethod(boolean dependsOnBean,
|
private CodeBlock generateNewInstanceCodeForMethod(boolean dependsOnBean,
|
||||||
Class<?> declaringClass, String factoryMethodName, CodeBlock args) {
|
Class<?> targetClass, String factoryMethodName, CodeBlock args) {
|
||||||
|
|
||||||
if (!dependsOnBean) {
|
if (!dependsOnBean) {
|
||||||
return CodeBlock.of("$T.$L($L)", declaringClass, factoryMethodName, args);
|
return CodeBlock.of("$T.$L($L)", targetClass, factoryMethodName, args);
|
||||||
}
|
}
|
||||||
return CodeBlock.of("$L.getBeanFactory().getBean($T.class).$L($L)",
|
return CodeBlock.of("$L.getBeanFactory().getBean($T.class).$L($L)",
|
||||||
REGISTERED_BEAN_PARAMETER_NAME, declaringClass, factoryMethodName, args);
|
REGISTERED_BEAN_PARAMETER_NAME, targetClass, factoryMethodName, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CodeBlock generateReturnStatement(GeneratedMethod generatedMethod) {
|
private CodeBlock generateReturnStatement(GeneratedMethod generatedMethod) {
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,6 +17,8 @@
|
|||||||
package org.springframework.beans.factory.support;
|
package org.springframework.beans.factory.support;
|
||||||
|
|
||||||
import java.lang.reflect.Executable;
|
import java.lang.reflect.Executable;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@@ -206,12 +208,33 @@ public final class RegisteredBean {
|
|||||||
/**
|
/**
|
||||||
* Resolve the constructor or factory method to use for this bean.
|
* Resolve the constructor or factory method to use for this bean.
|
||||||
* @return the {@link java.lang.reflect.Constructor} or {@link java.lang.reflect.Method}
|
* @return the {@link java.lang.reflect.Constructor} or {@link java.lang.reflect.Method}
|
||||||
|
* @deprecated in favor of {@link #resolveInstantiationDescriptor()}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "6.1.7")
|
||||||
public Executable resolveConstructorOrFactoryMethod() {
|
public Executable resolveConstructorOrFactoryMethod() {
|
||||||
return new ConstructorResolver((AbstractAutowireCapableBeanFactory) getBeanFactory())
|
return new ConstructorResolver((AbstractAutowireCapableBeanFactory) getBeanFactory())
|
||||||
.resolveConstructorOrFactoryMethod(getBeanName(), getMergedBeanDefinition());
|
.resolveConstructorOrFactoryMethod(getBeanName(), getMergedBeanDefinition());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the {@linkplain InstantiationDescriptor descriptor} to use to
|
||||||
|
* instantiate this bean. It defines the {@link java.lang.reflect.Constructor}
|
||||||
|
* or {@link java.lang.reflect.Method} to use as well as additional metadata.
|
||||||
|
* @since 6.1.7
|
||||||
|
*/
|
||||||
|
public InstantiationDescriptor resolveInstantiationDescriptor() {
|
||||||
|
Executable executable = resolveConstructorOrFactoryMethod();
|
||||||
|
if (executable instanceof Method method && !Modifier.isStatic(method.getModifiers())) {
|
||||||
|
String factoryBeanName = getMergedBeanDefinition().getFactoryBeanName();
|
||||||
|
if (factoryBeanName != null && this.beanFactory.containsBean(factoryBeanName)) {
|
||||||
|
Class<?> target = this.beanFactory.getMergedBeanDefinition(factoryBeanName)
|
||||||
|
.getResolvableType().toClass();
|
||||||
|
return new InstantiationDescriptor(executable, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new InstantiationDescriptor(executable, executable.getDeclaringClass());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve an autowired argument.
|
* Resolve an autowired argument.
|
||||||
* @param descriptor the descriptor for the dependency (field/method/constructor)
|
* @param descriptor the descriptor for the dependency (field/method/constructor)
|
||||||
@@ -237,6 +260,20 @@ public final class RegisteredBean {
|
|||||||
.append("mergedBeanDefinition", getMergedBeanDefinition()).toString();
|
.append("mergedBeanDefinition", getMergedBeanDefinition()).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describe how a bean should be instantiated. While the {@code targetClass}
|
||||||
|
* is usually the declaring class of the {@code executable}, there are cases
|
||||||
|
* where retaining the actual concrete type is necessary.
|
||||||
|
* @param executable the {@link Executable} to invoke
|
||||||
|
* @param targetClass the target {@link Class} of the executable
|
||||||
|
* @since 6.1.7
|
||||||
|
*/
|
||||||
|
public record InstantiationDescriptor(Executable executable, Class<?> targetClass) {
|
||||||
|
|
||||||
|
public InstantiationDescriptor(Executable executable) {
|
||||||
|
this(executable, executable.getDeclaringClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolver used to obtain inner-bean details.
|
* Resolver used to obtain inner-bean details.
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -161,7 +161,8 @@ class BeanDefinitionMethodGeneratorTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void generateWithBeanClassAndFactoryMethodNameSetsTargetTypeAndBeanClass() {
|
void generateWithBeanClassAndFactoryMethodNameSetsTargetTypeAndBeanClass() {
|
||||||
this.beanFactory.registerSingleton("factory", new SimpleBeanConfiguration());
|
this.beanFactory.registerBeanDefinition("factory",
|
||||||
|
new RootBeanDefinition(SimpleBeanConfiguration.class));
|
||||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(SimpleBean.class);
|
RootBeanDefinition beanDefinition = new RootBeanDefinition(SimpleBean.class);
|
||||||
beanDefinition.setFactoryBeanName("factory");
|
beanDefinition.setFactoryBeanName("factory");
|
||||||
beanDefinition.setFactoryMethodName("simpleBean");
|
beanDefinition.setFactoryMethodName("simpleBean");
|
||||||
@@ -182,7 +183,8 @@ class BeanDefinitionMethodGeneratorTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void generateWithTargetTypeAndFactoryMethodNameSetsOnlyBeanClass() {
|
void generateWithTargetTypeAndFactoryMethodNameSetsOnlyBeanClass() {
|
||||||
this.beanFactory.registerSingleton("factory", new SimpleBeanConfiguration());
|
this.beanFactory.registerBeanDefinition("factory",
|
||||||
|
new RootBeanDefinition(SimpleBeanConfiguration.class));
|
||||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||||
beanDefinition.setTargetType(SimpleBean.class);
|
beanDefinition.setTargetType(SimpleBean.class);
|
||||||
beanDefinition.setFactoryBeanName("factory");
|
beanDefinition.setFactoryBeanName("factory");
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import org.springframework.beans.factory.support.RegisteredBean;
|
|||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.beans.testfixture.beans.factory.DummyFactory;
|
import org.springframework.beans.testfixture.beans.factory.DummyFactory;
|
||||||
import org.springframework.beans.testfixture.beans.factory.StringFactoryBean;
|
import org.springframework.beans.testfixture.beans.factory.StringFactoryBean;
|
||||||
|
import org.springframework.beans.testfixture.beans.factory.aot.DefaultSimpleBeanContract;
|
||||||
import org.springframework.beans.testfixture.beans.factory.aot.GenericFactoryBean;
|
import org.springframework.beans.testfixture.beans.factory.aot.GenericFactoryBean;
|
||||||
import org.springframework.beans.testfixture.beans.factory.aot.MockBeanRegistrationCode;
|
import org.springframework.beans.testfixture.beans.factory.aot.MockBeanRegistrationCode;
|
||||||
import org.springframework.beans.testfixture.beans.factory.aot.MockBeanRegistrationsCode;
|
import org.springframework.beans.testfixture.beans.factory.aot.MockBeanRegistrationsCode;
|
||||||
@@ -38,6 +39,7 @@ import org.springframework.beans.testfixture.beans.factory.aot.NumberFactoryBean
|
|||||||
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBean;
|
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBean;
|
||||||
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBeanArrayFactoryBean;
|
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBeanArrayFactoryBean;
|
||||||
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBeanConfiguration;
|
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBeanConfiguration;
|
||||||
|
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBeanContract;
|
||||||
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBeanFactoryBean;
|
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBeanFactoryBean;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.javapoet.ClassName;
|
import org.springframework.javapoet.ClassName;
|
||||||
@@ -126,6 +128,21 @@ class DefaultBeanRegistrationCodeFragmentsTests {
|
|||||||
SimpleBeanConfiguration.class);
|
SimpleBeanConfiguration.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // gh-32609
|
||||||
|
void getTargetOnMethodFromInterface() {
|
||||||
|
this.beanFactory.registerBeanDefinition("configuration",
|
||||||
|
new RootBeanDefinition(DefaultSimpleBeanContract.class));
|
||||||
|
Method method = ReflectionUtils.findMethod(SimpleBeanContract.class, "simpleBean");
|
||||||
|
assertThat(method).isNotNull();
|
||||||
|
RootBeanDefinition beanDefinition = new RootBeanDefinition(SimpleBean.class);
|
||||||
|
applyConstructorOrFactoryMethod(beanDefinition, method);
|
||||||
|
beanDefinition.setFactoryBeanName("configuration");
|
||||||
|
this.beanFactory.registerBeanDefinition("testBean", beanDefinition);
|
||||||
|
RegisteredBean registeredBean = RegisteredBean.of(this.beanFactory, "testBean");
|
||||||
|
assertTarget(createInstance(registeredBean).getTarget(registeredBean),
|
||||||
|
DefaultSimpleBeanContract.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getTargetOnMethodWithInnerBeanInJavaPackage() {
|
void getTargetOnMethodWithInnerBeanInJavaPackage() {
|
||||||
RegisteredBean registeredBean = registerTestBean(SimpleBean.class);
|
RegisteredBean registeredBean = registerTestBean(SimpleBean.class);
|
||||||
@@ -190,7 +207,7 @@ class DefaultBeanRegistrationCodeFragmentsTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void customizedGetTargetDoesNotResolveConstructorOrFactoryMethod() {
|
void customizedGetTargetDoesNotResolveInstantiationDescriptor() {
|
||||||
RegisteredBean registeredBean = spy(registerTestBean(SimpleBean.class));
|
RegisteredBean registeredBean = spy(registerTestBean(SimpleBean.class));
|
||||||
BeanRegistrationCodeFragments customCodeFragments = createCustomCodeFragments(registeredBean, codeFragments -> new BeanRegistrationCodeFragmentsDecorator(codeFragments) {
|
BeanRegistrationCodeFragments customCodeFragments = createCustomCodeFragments(registeredBean, codeFragments -> new BeanRegistrationCodeFragmentsDecorator(codeFragments) {
|
||||||
@Override
|
@Override
|
||||||
@@ -199,11 +216,11 @@ class DefaultBeanRegistrationCodeFragmentsTests {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
assertTarget(customCodeFragments.getTarget(registeredBean), String.class);
|
assertTarget(customCodeFragments.getTarget(registeredBean), String.class);
|
||||||
verify(registeredBean, never()).resolveConstructorOrFactoryMethod();
|
verify(registeredBean, never()).resolveInstantiationDescriptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void customizedGenerateInstanceSupplierCodeDoesNotResolveConstructorOrFactoryMethod() {
|
void customizedGenerateInstanceSupplierCodeDoesNotResolveInstantiationDescriptor() {
|
||||||
RegisteredBean registeredBean = spy(registerTestBean(SimpleBean.class));
|
RegisteredBean registeredBean = spy(registerTestBean(SimpleBean.class));
|
||||||
BeanRegistrationCodeFragments customCodeFragments = createCustomCodeFragments(registeredBean, codeFragments -> new BeanRegistrationCodeFragmentsDecorator(codeFragments) {
|
BeanRegistrationCodeFragments customCodeFragments = createCustomCodeFragments(registeredBean, codeFragments -> new BeanRegistrationCodeFragmentsDecorator(codeFragments) {
|
||||||
@Override
|
@Override
|
||||||
@@ -214,7 +231,7 @@ class DefaultBeanRegistrationCodeFragmentsTests {
|
|||||||
});
|
});
|
||||||
assertThat(customCodeFragments.generateInstanceSupplierCode(this.generationContext,
|
assertThat(customCodeFragments.generateInstanceSupplierCode(this.generationContext,
|
||||||
new MockBeanRegistrationCode(this.generationContext), false)).hasToString("// Hello");
|
new MockBeanRegistrationCode(this.generationContext), false)).hasToString("// Hello");
|
||||||
verify(registeredBean, never()).resolveConstructorOrFactoryMethod();
|
verify(registeredBean, never()).resolveInstantiationDescriptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
private BeanRegistrationCodeFragments createCustomCodeFragments(RegisteredBean registeredBean, UnaryOperator<BeanRegistrationCodeFragments> customFragments) {
|
private BeanRegistrationCodeFragments createCustomCodeFragments(RegisteredBean registeredBean, UnaryOperator<BeanRegistrationCodeFragments> customFragments) {
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.beans.factory.aot;
|
package org.springframework.beans.factory.aot;
|
||||||
|
|
||||||
import java.lang.reflect.Executable;
|
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@@ -38,10 +37,14 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
|||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.InstanceSupplier;
|
import org.springframework.beans.factory.support.InstanceSupplier;
|
||||||
import org.springframework.beans.factory.support.RegisteredBean;
|
import org.springframework.beans.factory.support.RegisteredBean;
|
||||||
|
import org.springframework.beans.factory.support.RegisteredBean.InstantiationDescriptor;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.beans.testfixture.beans.TestBean;
|
import org.springframework.beans.testfixture.beans.TestBean;
|
||||||
import org.springframework.beans.testfixture.beans.TestBeanWithPrivateConstructor;
|
import org.springframework.beans.testfixture.beans.TestBeanWithPrivateConstructor;
|
||||||
|
import org.springframework.beans.testfixture.beans.factory.aot.DefaultSimpleBeanContract;
|
||||||
import org.springframework.beans.testfixture.beans.factory.aot.DeferredTypeBuilder;
|
import org.springframework.beans.testfixture.beans.factory.aot.DeferredTypeBuilder;
|
||||||
|
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBean;
|
||||||
|
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBeanContract;
|
||||||
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration;
|
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration;
|
||||||
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.EnvironmentAwareComponent;
|
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.EnvironmentAwareComponent;
|
||||||
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.NoDependencyComponent;
|
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.NoDependencyComponent;
|
||||||
@@ -185,6 +188,23 @@ class InstanceSupplierCodeGeneratorTests {
|
|||||||
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));
|
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void generateWhenHasFactoryMethodOnInterface() {
|
||||||
|
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||||
|
.rootBeanDefinition(SimpleBean.class)
|
||||||
|
.setFactoryMethodOnBean("simpleBean", "config").getBeanDefinition();
|
||||||
|
this.beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||||
|
.rootBeanDefinition(DefaultSimpleBeanContract.class).getBeanDefinition());
|
||||||
|
compile(beanDefinition, (instanceSupplier, compiled) -> {
|
||||||
|
Object bean = getBean(beanDefinition, instanceSupplier);
|
||||||
|
assertThat(bean).isInstanceOf(SimpleBean.class);
|
||||||
|
assertThat(compiled.getSourceFile()).contains(
|
||||||
|
"getBeanFactory().getBean(DefaultSimpleBeanContract.class).simpleBean()");
|
||||||
|
});
|
||||||
|
assertThat(getReflectionHints().getTypeHint(SimpleBeanContract.class))
|
||||||
|
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void generateWhenHasPrivateStaticFactoryMethodWithNoArg() {
|
void generateWhenHasPrivateStaticFactoryMethodWithNoArg() {
|
||||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||||
@@ -402,9 +422,9 @@ class InstanceSupplierCodeGeneratorTests {
|
|||||||
InstanceSupplierCodeGenerator generator = new InstanceSupplierCodeGenerator(
|
InstanceSupplierCodeGenerator generator = new InstanceSupplierCodeGenerator(
|
||||||
this.generationContext, generateClass.getName(),
|
this.generationContext, generateClass.getName(),
|
||||||
generateClass.getMethods(), false);
|
generateClass.getMethods(), false);
|
||||||
Executable constructorOrFactoryMethod = registeredBean.resolveConstructorOrFactoryMethod();
|
InstantiationDescriptor instantiationDescriptor = registeredBean.resolveInstantiationDescriptor();
|
||||||
assertThat(constructorOrFactoryMethod).isNotNull();
|
assertThat(instantiationDescriptor).isNotNull();
|
||||||
CodeBlock generatedCode = generator.generateCode(registeredBean, constructorOrFactoryMethod);
|
CodeBlock generatedCode = generator.generateCode(registeredBean, instantiationDescriptor);
|
||||||
typeBuilder.set(type -> {
|
typeBuilder.set(type -> {
|
||||||
type.addModifiers(Modifier.PUBLIC);
|
type.addModifiers(Modifier.PUBLIC);
|
||||||
type.addSuperinterface(ParameterizedTypeName.get(Supplier.class, InstanceSupplier.class));
|
type.addSuperinterface(ParameterizedTypeName.get(Supplier.class, InstanceSupplier.class));
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -116,9 +116,9 @@ class InstanceSupplierCodeGeneratorKotlinTests {
|
|||||||
generationContext, generateClass.name,
|
generationContext, generateClass.name,
|
||||||
generateClass.methods, false
|
generateClass.methods, false
|
||||||
)
|
)
|
||||||
val constructorOrFactoryMethod = registeredBean.resolveConstructorOrFactoryMethod()
|
val instantiationDescriptor = registeredBean.resolveInstantiationDescriptor()
|
||||||
Assertions.assertThat(constructorOrFactoryMethod).isNotNull()
|
Assertions.assertThat(instantiationDescriptor).isNotNull()
|
||||||
val generatedCode = generator.generateCode(registeredBean, constructorOrFactoryMethod)
|
val generatedCode = generator.generateCode(registeredBean, instantiationDescriptor)
|
||||||
typeBuilder.set { type: TypeSpec.Builder ->
|
typeBuilder.set { type: TypeSpec.Builder ->
|
||||||
type.addModifiers(Modifier.PUBLIC)
|
type.addModifiers(Modifier.PUBLIC)
|
||||||
type.addSuperinterface(
|
type.addSuperinterface(
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.beans.testfixture.beans.factory.aot;
|
||||||
|
|
||||||
|
public class DefaultSimpleBeanContract implements SimpleBeanContract {
|
||||||
|
|
||||||
|
public SimpleBean anotherSimpleBean() {
|
||||||
|
return new SimpleBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.beans.testfixture.beans.factory.aot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Showcase a factory method that is defined on an interface.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public interface SimpleBeanContract {
|
||||||
|
|
||||||
|
default SimpleBean simpleBean() {
|
||||||
|
return new SimpleBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -75,6 +75,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProce
|
|||||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.RegisteredBean;
|
import org.springframework.beans.factory.support.RegisteredBean;
|
||||||
|
import org.springframework.beans.factory.support.RegisteredBean.InstantiationDescriptor;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.context.ApplicationStartupAware;
|
import org.springframework.context.ApplicationStartupAware;
|
||||||
import org.springframework.context.EnvironmentAware;
|
import org.springframework.context.EnvironmentAware;
|
||||||
@@ -795,24 +796,26 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||||||
public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
|
public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
|
||||||
BeanRegistrationCode beanRegistrationCode, boolean allowDirectSupplierShortcut) {
|
BeanRegistrationCode beanRegistrationCode, boolean allowDirectSupplierShortcut) {
|
||||||
|
|
||||||
Executable executableToUse = proxyExecutable(generationContext.getRuntimeHints(),
|
InstantiationDescriptor instantiationDescriptor = proxyInstantiationDescriptor(
|
||||||
this.registeredBean.resolveConstructorOrFactoryMethod());
|
generationContext.getRuntimeHints(), this.registeredBean.resolveInstantiationDescriptor());
|
||||||
return new InstanceSupplierCodeGenerator(generationContext,
|
return new InstanceSupplierCodeGenerator(generationContext,
|
||||||
beanRegistrationCode.getClassName(), beanRegistrationCode.getMethods(), allowDirectSupplierShortcut)
|
beanRegistrationCode.getClassName(), beanRegistrationCode.getMethods(), allowDirectSupplierShortcut)
|
||||||
.generateCode(this.registeredBean, executableToUse);
|
.generateCode(this.registeredBean, instantiationDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Executable proxyExecutable(RuntimeHints runtimeHints, Executable userExecutable) {
|
private InstantiationDescriptor proxyInstantiationDescriptor(RuntimeHints runtimeHints, InstantiationDescriptor instantiationDescriptor) {
|
||||||
|
Executable userExecutable = instantiationDescriptor.executable();
|
||||||
if (userExecutable instanceof Constructor<?> userConstructor) {
|
if (userExecutable instanceof Constructor<?> userConstructor) {
|
||||||
try {
|
try {
|
||||||
runtimeHints.reflection().registerConstructor(userConstructor, ExecutableMode.INTROSPECT);
|
runtimeHints.reflection().registerConstructor(userConstructor, ExecutableMode.INTROSPECT);
|
||||||
return this.proxyClass.getConstructor(userExecutable.getParameterTypes());
|
Constructor<?> constructor = this.proxyClass.getConstructor(userExecutable.getParameterTypes());
|
||||||
|
return new InstantiationDescriptor(constructor);
|
||||||
}
|
}
|
||||||
catch (NoSuchMethodException ex) {
|
catch (NoSuchMethodException ex) {
|
||||||
throw new IllegalStateException("No matching constructor found on proxy " + this.proxyClass, ex);
|
throw new IllegalStateException("No matching constructor found on proxy " + this.proxyClass, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return userExecutable;
|
return instantiationDescriptor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -432,7 +432,6 @@ class TestContextAotGeneratorIntegrationTests extends AbstractAotTests {
|
|||||||
"org/springframework/test/context/aot/samples/web/WebTestConfiguration__TestContext006_BeanDefinitions.java",
|
"org/springframework/test/context/aot/samples/web/WebTestConfiguration__TestContext006_BeanDefinitions.java",
|
||||||
"org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration__TestContext006_Autowiring.java",
|
"org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration__TestContext006_Autowiring.java",
|
||||||
"org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration__TestContext006_BeanDefinitions.java",
|
"org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration__TestContext006_BeanDefinitions.java",
|
||||||
"org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport__TestContext006_BeanDefinitions.java",
|
|
||||||
// XmlSpringJupiterTests
|
// XmlSpringJupiterTests
|
||||||
"org/springframework/context/event/DefaultEventListenerFactory__TestContext007_BeanDefinitions.java",
|
"org/springframework/context/event/DefaultEventListenerFactory__TestContext007_BeanDefinitions.java",
|
||||||
"org/springframework/context/event/EventListenerMethodProcessor__TestContext007_BeanDefinitions.java",
|
"org/springframework/context/event/EventListenerMethodProcessor__TestContext007_BeanDefinitions.java",
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -22,6 +22,7 @@ import org.springframework.aot.generate.GenerationContext;
|
|||||||
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
|
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
|
||||||
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
|
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
|
||||||
import org.springframework.beans.factory.aot.BeanRegistrationCode;
|
import org.springframework.beans.factory.aot.BeanRegistrationCode;
|
||||||
|
import org.springframework.beans.factory.support.RegisteredBean.InstantiationDescriptor;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.aot.ApplicationContextAotGenerator;
|
import org.springframework.context.aot.ApplicationContextAotGenerator;
|
||||||
@@ -44,7 +45,8 @@ public class ManagementConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
static BeanRegistrationAotProcessor beanRegistrationAotProcessor() {
|
static BeanRegistrationAotProcessor beanRegistrationAotProcessor() {
|
||||||
return registeredBean -> {
|
return registeredBean -> {
|
||||||
Executable factoryMethod = registeredBean.resolveConstructorOrFactoryMethod();
|
InstantiationDescriptor instantiationDescriptor = registeredBean.resolveInstantiationDescriptor();
|
||||||
|
Executable factoryMethod = instantiationDescriptor.executable();
|
||||||
// Make AOT contribution for @Managed @Bean methods.
|
// Make AOT contribution for @Managed @Bean methods.
|
||||||
if (AnnotatedElementUtils.hasAnnotation(factoryMethod, Managed.class)) {
|
if (AnnotatedElementUtils.hasAnnotation(factoryMethod, Managed.class)) {
|
||||||
return new AotContribution(createManagementContext());
|
return new AotContribution(createManagementContext());
|
||||||
|
|||||||
Reference in New Issue
Block a user