Restore user type in generated root bean definitions
This commit restores the user class in generated RootBeanDefinition instances. Previously the CGLIB subclass was exposed. While this is important in regular runtime as the configuration class parser operates on the bean definition, this is not relevant for AOT as this information is internal and captured in the instance supplier. Closes gh-33960
This commit is contained in:
@@ -123,7 +123,7 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
|
||||
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
RootBeanDefinition mbd = this.registeredBean.getMergedBeanDefinition();
|
||||
Class<?> beanClass = (mbd.hasBeanClass() ? mbd.getBeanClass() : null);
|
||||
Class<?> beanClass = (mbd.hasBeanClass() ? ClassUtils.getUserClass(mbd.getBeanClass()) : null);
|
||||
CodeBlock beanClassCode = generateBeanClassCode(
|
||||
beanRegistrationCode.getClassName().packageName(),
|
||||
(beanClass != null ? beanClass : beanType.toClass()));
|
||||
|
||||
@@ -84,6 +84,7 @@ import org.springframework.context.testfixture.context.annotation.LazyResourceMe
|
||||
import org.springframework.context.testfixture.context.annotation.PropertySourceConfiguration;
|
||||
import org.springframework.context.testfixture.context.annotation.QualifierConfiguration;
|
||||
import org.springframework.context.testfixture.context.annotation.ResourceComponent;
|
||||
import org.springframework.context.testfixture.context.annotation.ValueCglibConfiguration;
|
||||
import org.springframework.context.testfixture.context.generator.SimpleComponent;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
@@ -438,12 +439,14 @@ class ApplicationContextAotGeneratorTests {
|
||||
@CompileWithForkedClassLoader
|
||||
class ConfigurationClassCglibProxy {
|
||||
|
||||
private static final String CGLIB_CONFIGURATION_CLASS_SUFFIX = "$$SpringCGLIB$$0";
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenHasCglibProxyWriteProxyAndGenerateReflectionHints() throws IOException {
|
||||
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
applicationContext.registerBean(CglibConfiguration.class);
|
||||
TestGenerationContext context = processAheadOfTime(applicationContext);
|
||||
isRegisteredCglibClass(context, CglibConfiguration.class.getName() + "$$SpringCGLIB$$0");
|
||||
isRegisteredCglibClass(context, CglibConfiguration.class.getName() + CGLIB_CONFIGURATION_CLASS_SUFFIX);
|
||||
isRegisteredCglibClass(context, CglibConfiguration.class.getName() + "$$SpringCGLIB$$FastClass$$0");
|
||||
isRegisteredCglibClass(context, CglibConfiguration.class.getName() + "$$SpringCGLIB$$FastClass$$1");
|
||||
}
|
||||
@@ -455,6 +458,43 @@ class ApplicationContextAotGeneratorTests {
|
||||
.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(context.getRuntimeHints());
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeExposeUserClassForCglibProxy() {
|
||||
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
applicationContext.registerBean("config", ValueCglibConfiguration.class);
|
||||
|
||||
testCompiledResult(applicationContext, (initializer, compiled) -> {
|
||||
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
|
||||
assertThat(freshApplicationContext).satisfies(hasBeanDefinitionOfBeanClass("config", ValueCglibConfiguration.class));
|
||||
assertThat(compiled.getSourceFile(".*ValueCglibConfiguration__BeanDefinitions"))
|
||||
.contains("new RootBeanDefinition(ValueCglibConfiguration.class)")
|
||||
.contains("new %s(".formatted(toCglibClassSimpleName(ValueCglibConfiguration.class)));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeUsesCglibClassForFactoryMethod() {
|
||||
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
applicationContext.registerBean("config", CglibConfiguration.class);
|
||||
|
||||
testCompiledResult(applicationContext, (initializer, compiled) -> {
|
||||
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
|
||||
assertThat(freshApplicationContext).satisfies(hasBeanDefinitionOfBeanClass("config", CglibConfiguration.class));
|
||||
assertThat(compiled.getSourceFile(".*CglibConfiguration__BeanDefinitions"))
|
||||
.contains("new RootBeanDefinition(CglibConfiguration.class)")
|
||||
.contains(">forFactoryMethod(%s.class,".formatted(toCglibClassSimpleName(CglibConfiguration.class)))
|
||||
.doesNotContain(">forFactoryMethod(%s.class,".formatted(CglibConfiguration.class));
|
||||
});
|
||||
}
|
||||
|
||||
private Consumer<GenericApplicationContext> hasBeanDefinitionOfBeanClass(String name, Class<?> beanClass) {
|
||||
return context -> {
|
||||
assertThat(context.containsBean(name)).isTrue();
|
||||
assertThat(context.getBeanDefinition(name)).isInstanceOfSatisfying(RootBeanDefinition.class,
|
||||
rbd -> assertThat(rbd.getBeanClass()).isEqualTo(beanClass));
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenHasCglibProxyUseProxy() {
|
||||
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
@@ -493,6 +533,20 @@ class ApplicationContextAotGeneratorTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenHasCglibProxyWithAnnotationsOnTheUserClasConstructor() {
|
||||
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
applicationContext.registerBean("config", ValueCglibConfiguration.class);
|
||||
testCompiledResult(applicationContext, (initializer, compiled) -> {
|
||||
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(context -> {
|
||||
context.setEnvironment(new MockEnvironment().withProperty("name", "AOT World"));
|
||||
initializer.initialize(context);
|
||||
});
|
||||
assertThat(freshApplicationContext.getBean(ValueCglibConfiguration.class)
|
||||
.getName()).isEqualTo("AOT World");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenHasCglibProxyWithArgumentsUseProxy() {
|
||||
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
@@ -516,6 +570,10 @@ class ApplicationContextAotGeneratorTests {
|
||||
.accepts(generationContext.getRuntimeHints());
|
||||
}
|
||||
|
||||
private String toCglibClassSimpleName(Class<?> configClass) {
|
||||
return configClass.getSimpleName() + CGLIB_CONFIGURATION_CLASS_SUFFIX;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.context.testfixture.context.annotation;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class ValueCglibConfiguration {
|
||||
|
||||
private final String name;
|
||||
|
||||
public ValueCglibConfiguration(@Value("${name:World}") String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user