Handle hints for CGLIB proxies consistently
This commit makes sure that hints are registered for CGLIB proxies even if the proxy itself is not created. This typically happens when AOT runs on an existing classpath, and a previous run already created the proxy. Closes gh-29295
This commit is contained in:
@@ -21,7 +21,10 @@ import java.util.regex.Pattern;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -31,6 +34,8 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation6.ComponentForScanning;
|
||||
import org.springframework.context.annotation6.ConfigForScanning;
|
||||
import org.springframework.context.annotation6.Jsr330NamedForScanning;
|
||||
import org.springframework.context.testfixture.context.annotation.CglibConfiguration;
|
||||
import org.springframework.context.testfixture.context.annotation.LambdaBeanConfiguration;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
@@ -450,6 +455,40 @@ class AnnotationConfigApplicationContextTests {
|
||||
assertThat(bean.applicationContext).isSameAs(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
void refreshForAotRegisterHintsForCglibProxy() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(CglibConfiguration.class);
|
||||
RuntimeHints runtimeHints = new RuntimeHints();
|
||||
context.refreshForAotProcessing(runtimeHints);
|
||||
TypeReference cglibType = TypeReference.of(CglibConfiguration.class.getName() + "$$SpringCGLIB$$0");
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(cglibType)
|
||||
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
|
||||
MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS))
|
||||
.accepts(runtimeHints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void refreshForAotRegisterHintsForTargetOfCglibProxy() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(CglibConfiguration.class);
|
||||
RuntimeHints runtimeHints = new RuntimeHints();
|
||||
context.refreshForAotProcessing(runtimeHints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(TypeReference.of(CglibConfiguration.class))
|
||||
.withMemberCategories(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS,
|
||||
MemberCategory.INVOKE_DECLARED_METHODS))
|
||||
.accepts(runtimeHints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void refreshForAotRegisterDoesNotConsiderLambdaBeanAsCglibProxy() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(LambdaBeanConfiguration.class);
|
||||
RuntimeHints runtimeHints = new RuntimeHints();
|
||||
context.refreshForAotProcessing(runtimeHints);
|
||||
assertThat(runtimeHints.reflection().typeHints()).isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@@ -297,10 +297,15 @@ class ApplicationContextAotGeneratorTests {
|
||||
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
applicationContext.registerBean(CglibConfiguration.class);
|
||||
TestGenerationContext context = processAheadOfTime(applicationContext);
|
||||
String proxyClassName = CglibConfiguration.class.getName() + "$$SpringCGLIB$$0";
|
||||
isRegisteredCglibClass(context, CglibConfiguration.class.getName() + "$$SpringCGLIB$$0");
|
||||
isRegisteredCglibClass(context, CglibConfiguration.class.getName() + "$$SpringCGLIB$$1");
|
||||
isRegisteredCglibClass(context, CglibConfiguration.class.getName() + "$$SpringCGLIB$$2");
|
||||
}
|
||||
|
||||
private void isRegisteredCglibClass(TestGenerationContext context, String cglibClassName) throws IOException {
|
||||
assertThat(context.getGeneratedFiles()
|
||||
.getGeneratedFileContent(Kind.CLASS, proxyClassName.replace('.', '/') + ".class")).isNotNull();
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(TypeReference.of(proxyClassName))
|
||||
.getGeneratedFileContent(Kind.CLASS, cglibClassName.replace('.', '/') + ".class")).isNotNull();
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(TypeReference.of(cglibClassName))
|
||||
.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(context.getRuntimeHints());
|
||||
}
|
||||
|
||||
|
||||
@@ -30,57 +30,46 @@ import org.springframework.aot.test.generate.TestGenerationContext;
|
||||
import org.springframework.core.io.InputStreamSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link GeneratedClassHandler}.
|
||||
* Tests for {@link CglibClassHandler}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class GeneratedClassHandlerTests {
|
||||
class CglibClassHandlerTests {
|
||||
|
||||
private static final byte[] TEST_CONTENT = new byte[] { 'a' };
|
||||
|
||||
private final TestGenerationContext generationContext;
|
||||
|
||||
private final GeneratedClassHandler handler;
|
||||
private final CglibClassHandler handler;
|
||||
|
||||
public GeneratedClassHandlerTests() {
|
||||
public CglibClassHandlerTests() {
|
||||
this.generationContext = new TestGenerationContext();
|
||||
this.handler = new GeneratedClassHandler(this.generationContext);
|
||||
this.handler = new CglibClassHandler(this.generationContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
void handlerGenerateRuntimeHintsForProxy() {
|
||||
void handlerGeneratedClassCreatesRuntimeHintsForProxy() {
|
||||
String className = "com.example.Test$$SpringCGLIB$$0";
|
||||
this.handler.accept(className, TEST_CONTENT);
|
||||
this.handler.handleGeneratedClass(className, TEST_CONTENT);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(TypeReference.of(className))
|
||||
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
|
||||
MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS))
|
||||
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
|
||||
.accepts(this.generationContext.getRuntimeHints());
|
||||
}
|
||||
|
||||
@Test
|
||||
void handlerGenerateRuntimeHintsForTargetType() {
|
||||
String className = "com.example.Test$$SpringCGLIB$$0";
|
||||
this.handler.accept(className, TEST_CONTENT);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(TypeReference.of("com.example.Test"))
|
||||
.withMemberCategories(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS,
|
||||
MemberCategory.INVOKE_DECLARED_METHODS))
|
||||
void handlerLoadedClassCreatesRuntimeHintsForProxy() {
|
||||
this.handler.handleLoadedClass(CglibClassHandler.class);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(CglibClassHandler.class)
|
||||
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
|
||||
.accepts(this.generationContext.getRuntimeHints());
|
||||
}
|
||||
|
||||
@Test
|
||||
void handlerFailsWithInvalidProxyClassName() {
|
||||
String className = "com.example.Test$$AnotherProxy$$0";
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.handler.accept(className, TEST_CONTENT))
|
||||
.withMessageContaining("Failed to extract target type");
|
||||
}
|
||||
|
||||
@Test
|
||||
void handlerRegisterGeneratedClass() throws IOException {
|
||||
String className = "com.example.Test$$SpringCGLIB$$0";
|
||||
this.handler.accept(className, TEST_CONTENT);
|
||||
this.handler.handleGeneratedClass(className, TEST_CONTENT);
|
||||
InMemoryGeneratedFiles generatedFiles = this.generationContext.getGeneratedFiles();
|
||||
assertThat(generatedFiles.getGeneratedFiles(Kind.SOURCE)).isEmpty();
|
||||
assertThat(generatedFiles.getGeneratedFiles(Kind.RESOURCE)).isEmpty();
|
||||
Reference in New Issue
Block a user