Skip to content

Commit 9a1b7c5

Browse files
committed
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
1 parent c58c827 commit 9a1b7c5

File tree

2 files changed

+36
-5
lines changed

2 files changed

+36
-5
lines changed

spring-context/src/main/java/org/springframework/context/aot/GeneratedClassHandler.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ class GeneratedClassHandler implements BiConsumer<String, byte[]> {
4343
MemberCategory.INVOKE_DECLARED_METHODS,
4444
MemberCategory.DECLARED_FIELDS);
4545

46+
private static final Consumer<Builder> asCglibProxyTargetType = hint ->
47+
hint.withMembers(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS,
48+
MemberCategory.INVOKE_DECLARED_METHODS);
49+
4650
private final RuntimeHints runtimeHints;
4751

4852
private final GeneratedFiles generatedFiles;
@@ -54,9 +58,18 @@ class GeneratedClassHandler implements BiConsumer<String, byte[]> {
5458

5559
@Override
5660
public void accept(String className, byte[] content) {
57-
this.runtimeHints.reflection().registerType(TypeReference.of(className), asCglibProxy);
61+
this.runtimeHints.reflection().registerType(TypeReference.of(className), asCglibProxy)
62+
.registerType(TypeReference.of(getTargetTypeClassName(className)), asCglibProxyTargetType);
5863
String path = className.replace(".", "/") + ".class";
5964
this.generatedFiles.addFile(Kind.CLASS, path, new ByteArrayResource(content));
6065
}
6166

67+
private String getTargetTypeClassName(String proxyClassName) {
68+
int index = proxyClassName.indexOf("$$SpringCGLIB$$");
69+
if (index == -1) {
70+
throw new IllegalArgumentException("Failed to extract target type from " + proxyClassName);
71+
}
72+
return proxyClassName.substring(0, index);
73+
}
74+
6275
}

spring-context/src/test/java/org/springframework/context/aot/GeneratedClassHandlerTests.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.springframework.core.testfixture.aot.generate.TestGenerationContext;
3131

3232
import static org.assertj.core.api.Assertions.assertThat;
33+
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
3334

3435
/**
3536
* Tests for {@link GeneratedClassHandler}.
@@ -50,23 +51,40 @@ public GeneratedClassHandlerTests() {
5051
}
5152

5253
@Test
53-
void handlerGenerateRuntimeHints() {
54-
String className = "com.example.Test$$Proxy$$1";
54+
void handlerGenerateRuntimeHintsForProxy() {
55+
String className = "com.example.Test$$SpringCGLIB$$0";
5556
this.handler.accept(className, TEST_CONTENT);
5657
assertThat(RuntimeHintsPredicates.reflection().onType(TypeReference.of(className))
5758
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
5859
MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS))
5960
.accepts(this.generationContext.getRuntimeHints());
6061
}
6162

63+
@Test
64+
void handlerGenerateRuntimeHintsForTargetType() {
65+
String className = "com.example.Test$$SpringCGLIB$$0";
66+
this.handler.accept(className, TEST_CONTENT);
67+
assertThat(RuntimeHintsPredicates.reflection().onType(TypeReference.of("com.example.Test"))
68+
.withMemberCategories(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS,
69+
MemberCategory.INVOKE_DECLARED_METHODS))
70+
.accepts(this.generationContext.getRuntimeHints());
71+
}
72+
73+
@Test
74+
void handlerFailsWithInvalidProxyClassName() {
75+
String className = "com.example.Test$$AnotherProxy$$0";
76+
assertThatIllegalArgumentException().isThrownBy(() -> this.handler.accept(className, TEST_CONTENT))
77+
.withMessageContaining("Failed to extract target type");
78+
}
79+
6280
@Test
6381
void handlerRegisterGeneratedClass() throws IOException {
64-
String className = "com.example.Test$$Proxy$$1";
82+
String className = "com.example.Test$$SpringCGLIB$$0";
6583
this.handler.accept(className, TEST_CONTENT);
6684
InMemoryGeneratedFiles generatedFiles = this.generationContext.getGeneratedFiles();
6785
assertThat(generatedFiles.getGeneratedFiles(Kind.SOURCE)).isEmpty();
6886
assertThat(generatedFiles.getGeneratedFiles(Kind.RESOURCE)).isEmpty();
69-
String expectedPath = "com/example/Test$$Proxy$$1.class";
87+
String expectedPath = "com/example/Test$$SpringCGLIB$$0.class";
7088
assertThat(generatedFiles.getGeneratedFiles(Kind.CLASS)).containsOnlyKeys(expectedPath);
7189
assertContent(generatedFiles.getGeneratedFiles(Kind.CLASS).get(expectedPath), TEST_CONTENT);
7290
}

0 commit comments

Comments
 (0)