Merge branch '6.1.x'

This commit is contained in:
Stéphane Nicoll
2024-04-22 15:16:50 +02:00
12 changed files with 214 additions and 63 deletions

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.
@@ -17,7 +17,6 @@
package org.springframework.beans.factory.aot;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Modifier;
import java.util.List;
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.support.InstanceSupplier;
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.core.ResolvableType;
import org.springframework.javapoet.ClassName;
@@ -62,7 +62,7 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
private final BeanDefinitionMethodGeneratorFactory beanDefinitionMethodGeneratorFactory;
private final Supplier<Executable> constructorOrFactoryMethod;
private final Supplier<InstantiationDescriptor> instantiationDescriptor;
DefaultBeanRegistrationCodeFragments(BeanRegistrationsCode beanRegistrationsCode,
@@ -72,7 +72,7 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
this.beanRegistrationsCode = beanRegistrationsCode;
this.registeredBean = registeredBean;
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 "
+ "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()) {
RegisteredBean parent = registeredBean.getParent();
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));
}
private Class<?> extractDeclaringClass(ResolvableType beanType, Executable executable) {
Class<?> declaringClass = ClassUtils.getUserClass(executable.getDeclaringClass());
if (executable instanceof Constructor<?>
&& AccessControl.forMember(executable).isPublic()
private Class<?> extractDeclaringClass(RegisteredBean registeredBean, InstantiationDescriptor instantiationDescriptor) {
Class<?> declaringClass = ClassUtils.getUserClass(instantiationDescriptor.targetClass());
if (instantiationDescriptor.executable() instanceof Constructor<?>
&& AccessControl.forMember(instantiationDescriptor.executable()).isPublic()
&& 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 "
+ "an instance supplier callback: " + this.registeredBean.getMergedBeanDefinition());
}
return new InstanceSupplierCodeGenerator(generationContext,
beanRegistrationCode.getClassName(), beanRegistrationCode.getMethods(), allowDirectSupplierShortcut)
.generateCode(this.registeredBean, this.constructorOrFactoryMethod.get());
return new InstanceSupplierCodeGenerator(generationContext, beanRegistrationCode.getClassName(),
beanRegistrationCode.getMethods(), allowDirectSupplierShortcut).generateCode(
this.registeredBean, this.instantiationDescriptor.get());
}
@Override

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.
@@ -46,6 +46,7 @@ import org.springframework.beans.factory.support.AutowireCandidateResolver;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.InstanceSupplier;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.beans.factory.support.RegisteredBean.InstantiationDescriptor;
import org.springframework.core.KotlinDetector;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
@@ -120,14 +121,29 @@ public class InstanceSupplierCodeGenerator {
* @param registeredBean the bean to handle
* @param constructorOrFactoryMethod the executable to use to create the bean
* @return the generated code
* @deprecated in favor of {@link #generateCode(RegisteredBean, InstantiationDescriptor)}
*/
@Deprecated(since = "6.1.7")
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);
if (constructorOrFactoryMethod instanceof Constructor<?> constructor) {
return generateCodeForConstructor(registeredBean, constructor);
}
if (constructorOrFactoryMethod instanceof Method method) {
return generateCodeForFactoryMethod(registeredBean, method);
return generateCodeForFactoryMethod(registeredBean, method, instantiationDescriptor.targetClass());
}
throw new IllegalStateException(
"No suitable executor found for " + registeredBean.getBeanName());
@@ -253,21 +269,21 @@ public class InstanceSupplierCodeGenerator {
declaringClass.getSimpleName(), args);
}
private CodeBlock generateCodeForFactoryMethod(RegisteredBean registeredBean, Method factoryMethod) {
private CodeBlock generateCodeForFactoryMethod(RegisteredBean registeredBean, Method factoryMethod, Class<?> targetClass) {
String beanName = registeredBean.getBeanName();
Class<?> declaringClass = ClassUtils.getUserClass(factoryMethod.getDeclaringClass());
Class<?> targetClassToUse = ClassUtils.getUserClass(targetClass);
boolean dependsOnBean = !Modifier.isStatic(factoryMethod.getModifiers());
Visibility accessVisibility = getAccessVisibility(registeredBean, factoryMethod);
if (accessVisibility != Visibility.PRIVATE) {
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,
Method factoryMethod, Class<?> declaringClass, boolean dependsOnBean) {
Method factoryMethod, Class<?> targetClass, boolean dependsOnBean) {
this.generationContext.getRuntimeHints().reflection().registerMethod(
factoryMethod, ExecutableMode.INTROSPECT);
@@ -276,20 +292,20 @@ public class InstanceSupplierCodeGenerator {
Class<?> suppliedType = ClassUtils.resolvePrimitiveIfNecessary(factoryMethod.getReturnType());
CodeBlock.Builder code = CodeBlock.builder();
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,
declaringClass, factoryMethod.getName());
targetClass, factoryMethod.getName());
return code.build();
}
GeneratedMethod getInstanceMethod = generateGetInstanceSupplierMethod(method ->
buildGetInstanceMethodForFactoryMethod(method, beanName, factoryMethod,
declaringClass, dependsOnBean, PRIVATE_STATIC));
targetClass, dependsOnBean, PRIVATE_STATIC));
return generateReturnStatement(getInstanceMethod);
}
private CodeBlock generateCodeForInaccessibleFactoryMethod(
String beanName, Method factoryMethod, Class<?> declaringClass) {
String beanName, Method factoryMethod, Class<?> targetClass) {
this.generationContext.getRuntimeHints().reflection().registerMethod(factoryMethod, ExecutableMode.INVOKE);
GeneratedMethod getInstanceMethod = generateGetInstanceSupplierMethod(method -> {
@@ -298,19 +314,19 @@ public class InstanceSupplierCodeGenerator {
method.addModifiers(PRIVATE_STATIC);
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, suppliedType));
method.addStatement(generateInstanceSupplierForFactoryMethod(
factoryMethod, suppliedType, declaringClass, factoryMethod.getName()));
factoryMethod, suppliedType, targetClass, factoryMethod.getName()));
});
return generateReturnStatement(getInstanceMethod);
}
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) {
String factoryMethodName = factoryMethod.getName();
Class<?> suppliedType = ClassUtils.resolvePrimitiveIfNecessary(factoryMethod.getReturnType());
CodeWarnings codeWarnings = new CodeWarnings();
codeWarnings.detectDeprecation(declaringClass, factoryMethod, suppliedType)
codeWarnings.detectDeprecation(targetClass, factoryMethod, suppliedType)
.detectDeprecation(Arrays.stream(factoryMethod.getParameters()).map(Parameter::getType));
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
@@ -320,41 +336,41 @@ public class InstanceSupplierCodeGenerator {
CodeBlock.Builder code = CodeBlock.builder();
code.add(generateInstanceSupplierForFactoryMethod(
factoryMethod, suppliedType, declaringClass, factoryMethodName));
factoryMethod, suppliedType, targetClass, factoryMethodName));
boolean hasArguments = factoryMethod.getParameterCount() > 0;
CodeBlock arguments = hasArguments ?
new AutowiredArgumentsCodeGenerator(declaringClass, factoryMethod)
new AutowiredArgumentsCodeGenerator(targetClass, factoryMethod)
.generateCode(factoryMethod.getParameterTypes())
: NO_ARGS;
CodeBlock newInstance = generateNewInstanceCodeForMethod(
dependsOnBean, declaringClass, factoryMethodName, arguments);
dependsOnBean, targetClass, factoryMethodName, arguments);
code.add(generateWithGeneratorCode(hasArguments, newInstance));
method.addStatement(code.build());
}
private CodeBlock generateInstanceSupplierForFactoryMethod(Method factoryMethod,
Class<?> suppliedType, Class<?> declaringClass, String factoryMethodName) {
Class<?> suppliedType, Class<?> targetClass, String factoryMethodName) {
if (factoryMethod.getParameterCount() == 0) {
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);
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,
Class<?> declaringClass, String factoryMethodName, CodeBlock args) {
Class<?> targetClass, String factoryMethodName, CodeBlock args) {
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)",
REGISTERED_BEAN_PARAMETER_NAME, declaringClass, factoryMethodName, args);
REGISTERED_BEAN_PARAMETER_NAME, targetClass, factoryMethodName, args);
}
private CodeBlock generateReturnStatement(GeneratedMethod generatedMethod) {

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.
@@ -17,6 +17,8 @@
package org.springframework.beans.factory.support;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Supplier;
@@ -206,12 +208,33 @@ public final class RegisteredBean {
/**
* Resolve the constructor or factory method to use for this bean.
* @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() {
return new ConstructorResolver((AbstractAutowireCapableBeanFactory) getBeanFactory())
.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.
* @param descriptor the descriptor for the dependency (field/method/constructor)
@@ -237,6 +260,20 @@ public final class RegisteredBean {
.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.

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.
@@ -161,7 +161,8 @@ class BeanDefinitionMethodGeneratorTests {
@Test
void generateWithBeanClassAndFactoryMethodNameSetsTargetTypeAndBeanClass() {
this.beanFactory.registerSingleton("factory", new SimpleBeanConfiguration());
this.beanFactory.registerBeanDefinition("factory",
new RootBeanDefinition(SimpleBeanConfiguration.class));
RootBeanDefinition beanDefinition = new RootBeanDefinition(SimpleBean.class);
beanDefinition.setFactoryBeanName("factory");
beanDefinition.setFactoryMethodName("simpleBean");
@@ -182,7 +183,8 @@ class BeanDefinitionMethodGeneratorTests {
@Test
void generateWithTargetTypeAndFactoryMethodNameSetsOnlyBeanClass() {
this.beanFactory.registerSingleton("factory", new SimpleBeanConfiguration());
this.beanFactory.registerBeanDefinition("factory",
new RootBeanDefinition(SimpleBeanConfiguration.class));
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setTargetType(SimpleBean.class);
beanDefinition.setFactoryBeanName("factory");

View File

@@ -31,6 +31,7 @@ import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.testfixture.beans.factory.DummyFactory;
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.MockBeanRegistrationCode;
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.SimpleBeanArrayFactoryBean;
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.core.ResolvableType;
import org.springframework.javapoet.ClassName;
@@ -126,6 +128,21 @@ class DefaultBeanRegistrationCodeFragmentsTests {
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
void getTargetOnMethodWithInnerBeanInJavaPackage() {
RegisteredBean registeredBean = registerTestBean(SimpleBean.class);
@@ -190,7 +207,7 @@ class DefaultBeanRegistrationCodeFragmentsTests {
}
@Test
void customizedGetTargetDoesNotResolveConstructorOrFactoryMethod() {
void customizedGetTargetDoesNotResolveInstantiationDescriptor() {
RegisteredBean registeredBean = spy(registerTestBean(SimpleBean.class));
BeanRegistrationCodeFragments customCodeFragments = createCustomCodeFragments(registeredBean, codeFragments -> new BeanRegistrationCodeFragmentsDecorator(codeFragments) {
@Override
@@ -199,11 +216,11 @@ class DefaultBeanRegistrationCodeFragmentsTests {
}
});
assertTarget(customCodeFragments.getTarget(registeredBean), String.class);
verify(registeredBean, never()).resolveConstructorOrFactoryMethod();
verify(registeredBean, never()).resolveInstantiationDescriptor();
}
@Test
void customizedGenerateInstanceSupplierCodeDoesNotResolveConstructorOrFactoryMethod() {
void customizedGenerateInstanceSupplierCodeDoesNotResolveInstantiationDescriptor() {
RegisteredBean registeredBean = spy(registerTestBean(SimpleBean.class));
BeanRegistrationCodeFragments customCodeFragments = createCustomCodeFragments(registeredBean, codeFragments -> new BeanRegistrationCodeFragmentsDecorator(codeFragments) {
@Override
@@ -214,7 +231,7 @@ class DefaultBeanRegistrationCodeFragmentsTests {
});
assertThat(customCodeFragments.generateInstanceSupplierCode(this.generationContext,
new MockBeanRegistrationCode(this.generationContext), false)).hasToString("// Hello");
verify(registeredBean, never()).resolveConstructorOrFactoryMethod();
verify(registeredBean, never()).resolveInstantiationDescriptor();
}
private BeanRegistrationCodeFragments createCustomCodeFragments(RegisteredBean registeredBean, UnaryOperator<BeanRegistrationCodeFragments> customFragments) {

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.
@@ -16,7 +16,6 @@
package org.springframework.beans.factory.aot;
import java.lang.reflect.Executable;
import java.util.function.BiConsumer;
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.InstanceSupplier;
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.testfixture.beans.TestBean;
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.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.EnvironmentAwareComponent;
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.NoDependencyComponent;
@@ -185,6 +188,23 @@ class InstanceSupplierCodeGeneratorTests {
.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
void generateWhenHasPrivateStaticFactoryMethodWithNoArg() {
BeanDefinition beanDefinition = BeanDefinitionBuilder
@@ -402,9 +422,9 @@ class InstanceSupplierCodeGeneratorTests {
InstanceSupplierCodeGenerator generator = new InstanceSupplierCodeGenerator(
this.generationContext, generateClass.getName(),
generateClass.getMethods(), false);
Executable constructorOrFactoryMethod = registeredBean.resolveConstructorOrFactoryMethod();
assertThat(constructorOrFactoryMethod).isNotNull();
CodeBlock generatedCode = generator.generateCode(registeredBean, constructorOrFactoryMethod);
InstantiationDescriptor instantiationDescriptor = registeredBean.resolveInstantiationDescriptor();
assertThat(instantiationDescriptor).isNotNull();
CodeBlock generatedCode = generator.generateCode(registeredBean, instantiationDescriptor);
typeBuilder.set(type -> {
type.addModifiers(Modifier.PUBLIC);
type.addSuperinterface(ParameterizedTypeName.get(Supplier.class, InstanceSupplier.class));

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.
@@ -116,9 +116,9 @@ class InstanceSupplierCodeGeneratorKotlinTests {
generationContext, generateClass.name,
generateClass.methods, false
)
val constructorOrFactoryMethod = registeredBean.resolveConstructorOrFactoryMethod()
Assertions.assertThat(constructorOrFactoryMethod).isNotNull()
val generatedCode = generator.generateCode(registeredBean, constructorOrFactoryMethod)
val instantiationDescriptor = registeredBean.resolveInstantiationDescriptor()
Assertions.assertThat(instantiationDescriptor).isNotNull()
val generatedCode = generator.generateCode(registeredBean, instantiationDescriptor)
typeBuilder.set { type: TypeSpec.Builder ->
type.addModifiers(Modifier.PUBLIC)
type.addSuperinterface(

View File

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

View File

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

View File

@@ -75,6 +75,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProce
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
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.context.ApplicationStartupAware;
import org.springframework.context.EnvironmentAware;
@@ -800,24 +801,26 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
BeanRegistrationCode beanRegistrationCode, boolean allowDirectSupplierShortcut) {
Executable executableToUse = proxyExecutable(generationContext.getRuntimeHints(),
this.registeredBean.resolveConstructorOrFactoryMethod());
InstantiationDescriptor instantiationDescriptor = proxyInstantiationDescriptor(
generationContext.getRuntimeHints(), this.registeredBean.resolveInstantiationDescriptor());
return new InstanceSupplierCodeGenerator(generationContext,
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) {
try {
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) {
throw new IllegalStateException("No matching constructor found on proxy " + this.proxyClass, ex);
}
}
return userExecutable;
return instantiationDescriptor;
}
}

View File

@@ -432,7 +432,6 @@ class TestContextAotGeneratorIntegrationTests extends AbstractAotTests {
"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_BeanDefinitions.java",
"org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport__TestContext006_BeanDefinitions.java",
// XmlSpringJupiterTests
"org/springframework/context/event/DefaultEventListenerFactory__TestContext007_BeanDefinitions.java",
"org/springframework/context/event/EventListenerMethodProcessor__TestContext007_BeanDefinitions.java",

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.
@@ -22,6 +22,7 @@ import org.springframework.aot.generate.GenerationContext;
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
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.Configuration;
import org.springframework.context.aot.ApplicationContextAotGenerator;
@@ -44,7 +45,8 @@ public class ManagementConfiguration {
@Bean
static BeanRegistrationAotProcessor beanRegistrationAotProcessor() {
return registeredBean -> {
Executable factoryMethod = registeredBean.resolveConstructorOrFactoryMethod();
InstantiationDescriptor instantiationDescriptor = registeredBean.resolveInstantiationDescriptor();
Executable factoryMethod = instantiationDescriptor.executable();
// Make AOT contribution for @Managed @Bean methods.
if (AnnotatedElementUtils.hasAnnotation(factoryMethod, Managed.class)) {
return new AotContribution(createManagementContext());