From 9a1b7c5e47c8ea56ad9b91c5f3ba87eda9d9d7c9 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 16 Aug 2022 07:32:42 +0200 Subject: [PATCH] Allow target type of a cglib proxy to be visible This commit updates the hints of a Cglib proxy's target type so that methods can be invoked and constructors can be introspected. The former is needed as a cglib proxy invokes the target type via reflection. As for that latter, this is required at least by Enhancer#filterConstructors. See gh-28954 --- .../context/aot/GeneratedClassHandler.java | 15 ++++++++++- .../aot/GeneratedClassHandlerTests.java | 26 ++++++++++++++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/aot/GeneratedClassHandler.java b/spring-context/src/main/java/org/springframework/context/aot/GeneratedClassHandler.java index f69d4d7a728..cbd39d74c22 100644 --- a/spring-context/src/main/java/org/springframework/context/aot/GeneratedClassHandler.java +++ b/spring-context/src/main/java/org/springframework/context/aot/GeneratedClassHandler.java @@ -43,6 +43,10 @@ class GeneratedClassHandler implements BiConsumer { MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS); + private static final Consumer asCglibProxyTargetType = hint -> + hint.withMembers(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_DECLARED_METHODS); + private final RuntimeHints runtimeHints; private final GeneratedFiles generatedFiles; @@ -54,9 +58,18 @@ class GeneratedClassHandler implements BiConsumer { @Override public void accept(String className, byte[] content) { - this.runtimeHints.reflection().registerType(TypeReference.of(className), asCglibProxy); + this.runtimeHints.reflection().registerType(TypeReference.of(className), asCglibProxy) + .registerType(TypeReference.of(getTargetTypeClassName(className)), asCglibProxyTargetType); String path = className.replace(".", "/") + ".class"; this.generatedFiles.addFile(Kind.CLASS, path, new ByteArrayResource(content)); } + private String getTargetTypeClassName(String proxyClassName) { + int index = proxyClassName.indexOf("$$SpringCGLIB$$"); + if (index == -1) { + throw new IllegalArgumentException("Failed to extract target type from " + proxyClassName); + } + return proxyClassName.substring(0, index); + } + } diff --git a/spring-context/src/test/java/org/springframework/context/aot/GeneratedClassHandlerTests.java b/spring-context/src/test/java/org/springframework/context/aot/GeneratedClassHandlerTests.java index 3a12883e75d..3959c3928c9 100644 --- a/spring-context/src/test/java/org/springframework/context/aot/GeneratedClassHandlerTests.java +++ b/spring-context/src/test/java/org/springframework/context/aot/GeneratedClassHandlerTests.java @@ -30,6 +30,7 @@ import org.springframework.core.io.InputStreamSource; import org.springframework.core.testfixture.aot.generate.TestGenerationContext; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; /** * Tests for {@link GeneratedClassHandler}. @@ -50,8 +51,8 @@ class GeneratedClassHandlerTests { } @Test - void handlerGenerateRuntimeHints() { - String className = "com.example.Test$$Proxy$$1"; + void handlerGenerateRuntimeHintsForProxy() { + String className = "com.example.Test$$SpringCGLIB$$0"; this.handler.accept(className, TEST_CONTENT); assertThat(RuntimeHintsPredicates.reflection().onType(TypeReference.of(className)) .withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, @@ -59,14 +60,31 @@ class GeneratedClassHandlerTests { .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)) + .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$$Proxy$$1"; + String className = "com.example.Test$$SpringCGLIB$$0"; this.handler.accept(className, TEST_CONTENT); InMemoryGeneratedFiles generatedFiles = this.generationContext.getGeneratedFiles(); assertThat(generatedFiles.getGeneratedFiles(Kind.SOURCE)).isEmpty(); assertThat(generatedFiles.getGeneratedFiles(Kind.RESOURCE)).isEmpty(); - String expectedPath = "com/example/Test$$Proxy$$1.class"; + String expectedPath = "com/example/Test$$SpringCGLIB$$0.class"; assertThat(generatedFiles.getGeneratedFiles(Kind.CLASS)).containsOnlyKeys(expectedPath); assertContent(generatedFiles.getGeneratedFiles(Kind.CLASS).get(expectedPath), TEST_CONTENT); }