Skip to content

Commit 81a9f3d

Browse files
committed
Restore public type for generated instance supplier of CGLIB proxy
This commit restores the signature of instance suppliers that are exposing a CGLIB proxy. While calling the CGLIB proxy itself, and making it available in BeanInstanceSupplier, is needed internally, such type should not be exposed as it is an internal concern. This was breaking InstanceSupplier.andThen as it expects the public type of the bean to be exposed, not it's eventual CGLIB subclass. Closes gh-33998
1 parent c807fa5 commit 81a9f3d

File tree

4 files changed

+147
-30
lines changed

4 files changed

+147
-30
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java

+42-30
Original file line numberDiff line numberDiff line change
@@ -156,91 +156,96 @@ private void registerRuntimeHintsIfNecessary(RegisteredBean registeredBean, Exec
156156
}
157157

158158
private CodeBlock generateCodeForConstructor(RegisteredBean registeredBean, Constructor<?> constructor) {
159-
String beanName = registeredBean.getBeanName();
160-
Class<?> beanClass = registeredBean.getBeanClass();
159+
ConstructorDescriptor descriptor = new ConstructorDescriptor(
160+
registeredBean.getBeanName(), constructor, registeredBean.getBeanClass());
161161

162-
if (KotlinDetector.isKotlinReflectPresent() && KotlinDelegate.hasConstructorWithOptionalParameter(beanClass)) {
163-
return generateCodeForInaccessibleConstructor(beanName, constructor,
164-
hints -> hints.registerType(beanClass, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
162+
Class<?> publicType = descriptor.publicType();
163+
if (KotlinDetector.isKotlinReflectPresent() && KotlinDelegate.hasConstructorWithOptionalParameter(publicType)) {
164+
return generateCodeForInaccessibleConstructor(descriptor,
165+
hints -> hints.registerType(publicType, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
165166
}
166167

167168
if (!isVisible(constructor, constructor.getDeclaringClass())) {
168-
return generateCodeForInaccessibleConstructor(beanName, constructor,
169+
return generateCodeForInaccessibleConstructor(descriptor,
169170
hints -> hints.registerConstructor(constructor, ExecutableMode.INVOKE));
170171
}
171-
return generateCodeForAccessibleConstructor(beanName, constructor);
172+
return generateCodeForAccessibleConstructor(descriptor);
172173
}
173174

174-
private CodeBlock generateCodeForAccessibleConstructor(String beanName, Constructor<?> constructor) {
175+
private CodeBlock generateCodeForAccessibleConstructor(ConstructorDescriptor descriptor) {
176+
Constructor<?> constructor = descriptor.constructor();
175177
this.generationContext.getRuntimeHints().reflection().registerConstructor(
176178
constructor, ExecutableMode.INTROSPECT);
177179

178180
if (constructor.getParameterCount() == 0) {
179181
if (!this.allowDirectSupplierShortcut) {
180-
return CodeBlock.of("$T.using($T::new)", InstanceSupplier.class, constructor.getDeclaringClass());
182+
return CodeBlock.of("$T.using($T::new)", InstanceSupplier.class, descriptor.actualType());
181183
}
182184
if (!isThrowingCheckedException(constructor)) {
183-
return CodeBlock.of("$T::new", constructor.getDeclaringClass());
185+
return CodeBlock.of("$T::new", descriptor.actualType());
184186
}
185-
return CodeBlock.of("$T.of($T::new)", ThrowingSupplier.class, constructor.getDeclaringClass());
187+
return CodeBlock.of("$T.of($T::new)", ThrowingSupplier.class, descriptor.actualType());
186188
}
187189

188190
GeneratedMethod generatedMethod = generateGetInstanceSupplierMethod(method ->
189-
buildGetInstanceMethodForConstructor(method, beanName, constructor, PRIVATE_STATIC));
191+
buildGetInstanceMethodForConstructor(method, descriptor, PRIVATE_STATIC));
190192
return generateReturnStatement(generatedMethod);
191193
}
192194

193-
private CodeBlock generateCodeForInaccessibleConstructor(String beanName,
194-
Constructor<?> constructor, Consumer<ReflectionHints> hints) {
195+
private CodeBlock generateCodeForInaccessibleConstructor(ConstructorDescriptor descriptor,
196+
Consumer<ReflectionHints> hints) {
195197

198+
Constructor<?> constructor = descriptor.constructor();
196199
CodeWarnings codeWarnings = new CodeWarnings();
197200
codeWarnings.detectDeprecation(constructor.getDeclaringClass(), constructor)
198201
.detectDeprecation(Arrays.stream(constructor.getParameters()).map(Parameter::getType));
199202
hints.accept(this.generationContext.getRuntimeHints().reflection());
200203

201204
GeneratedMethod generatedMethod = generateGetInstanceSupplierMethod(method -> {
202-
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
205+
method.addJavadoc("Get the bean instance supplier for '$L'.", descriptor.beanName());
203206
method.addModifiers(PRIVATE_STATIC);
204207
codeWarnings.suppress(method);
205-
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, constructor.getDeclaringClass()));
206-
method.addStatement(generateResolverForConstructor(constructor));
208+
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, descriptor.publicType()));
209+
method.addStatement(generateResolverForConstructor(descriptor));
207210
});
208211

209212
return generateReturnStatement(generatedMethod);
210213
}
211214

212-
private void buildGetInstanceMethodForConstructor(MethodSpec.Builder method, String beanName,
213-
Constructor<?> constructor, javax.lang.model.element.Modifier... modifiers) {
215+
private void buildGetInstanceMethodForConstructor(MethodSpec.Builder method, ConstructorDescriptor descriptor,
216+
javax.lang.model.element.Modifier... modifiers) {
214217

215-
Class<?> declaringClass = constructor.getDeclaringClass();
218+
Constructor<?> constructor = descriptor.constructor();
219+
Class<?> publicType = descriptor.publicType();
220+
Class<?> actualType = descriptor.actualType();
216221

217222
CodeWarnings codeWarnings = new CodeWarnings();
218-
codeWarnings.detectDeprecation(declaringClass, constructor)
223+
codeWarnings.detectDeprecation(actualType, constructor)
219224
.detectDeprecation(Arrays.stream(constructor.getParameters()).map(Parameter::getType));
220-
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
225+
method.addJavadoc("Get the bean instance supplier for '$L'.", descriptor.beanName());
221226
method.addModifiers(modifiers);
222227
codeWarnings.suppress(method);
223-
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, declaringClass));
228+
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, publicType));
224229

225230
CodeBlock.Builder code = CodeBlock.builder();
226-
code.add(generateResolverForConstructor(constructor));
231+
code.add(generateResolverForConstructor(descriptor));
227232
boolean hasArguments = constructor.getParameterCount() > 0;
228-
boolean onInnerClass = ClassUtils.isInnerClass(declaringClass);
233+
boolean onInnerClass = ClassUtils.isInnerClass(actualType);
229234

230235
CodeBlock arguments = hasArguments ?
231-
new AutowiredArgumentsCodeGenerator(declaringClass, constructor)
236+
new AutowiredArgumentsCodeGenerator(actualType, constructor)
232237
.generateCode(constructor.getParameterTypes(), (onInnerClass ? 1 : 0))
233238
: NO_ARGS;
234239

235-
CodeBlock newInstance = generateNewInstanceCodeForConstructor(declaringClass, arguments);
240+
CodeBlock newInstance = generateNewInstanceCodeForConstructor(actualType, arguments);
236241
code.add(generateWithGeneratorCode(hasArguments, newInstance));
237242
method.addStatement(code.build());
238243
}
239244

240-
private CodeBlock generateResolverForConstructor(Constructor<?> constructor) {
241-
CodeBlock parameterTypes = generateParameterTypesCode(constructor.getParameterTypes());
245+
private CodeBlock generateResolverForConstructor(ConstructorDescriptor descriptor) {
246+
CodeBlock parameterTypes = generateParameterTypesCode(descriptor.constructor().getParameterTypes());
242247
return CodeBlock.of("return $T.<$T>forConstructor($L)", BeanInstanceSupplier.class,
243-
constructor.getDeclaringClass(), parameterTypes);
248+
descriptor.publicType(), parameterTypes);
244249
}
245250

246251
private CodeBlock generateNewInstanceCodeForConstructor(Class<?> declaringClass, CodeBlock args) {
@@ -438,4 +443,11 @@ private void registerProxyIfNecessary(RuntimeHints runtimeHints, DependencyDescr
438443
}
439444
}
440445

446+
record ConstructorDescriptor(String beanName, Constructor<?> constructor, Class<?> publicType) {
447+
448+
Class<?> actualType() {
449+
return this.constructor.getDeclaringClass();
450+
}
451+
}
452+
441453
}

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

+29
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,10 @@
6666
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
6767
import org.springframework.context.support.GenericApplicationContext;
6868
import org.springframework.context.support.GenericXmlApplicationContext;
69+
import org.springframework.context.testfixture.context.annotation.AutowiredCglibConfiguration;
6970
import org.springframework.context.testfixture.context.annotation.AutowiredComponent;
7071
import org.springframework.context.testfixture.context.annotation.AutowiredGenericTemplate;
72+
import org.springframework.context.testfixture.context.annotation.AutowiredMixedCglibConfiguration;
7173
import org.springframework.context.testfixture.context.annotation.CglibConfiguration;
7274
import org.springframework.context.testfixture.context.annotation.ConfigurableCglibConfiguration;
7375
import org.springframework.context.testfixture.context.annotation.GenericTemplateConfiguration;
@@ -464,6 +466,33 @@ void processAheadOfTimeWhenHasCglibProxyUseProxy() {
464466
});
465467
}
466468

469+
@Test
470+
void processAheadOfTimeWhenHasCglibProxyAndAutowiring() {
471+
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
472+
applicationContext.registerBean(AutowiredCglibConfiguration.class);
473+
testCompiledResult(applicationContext, (initializer, compiled) -> {
474+
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(context -> {
475+
context.setEnvironment(new MockEnvironment().withProperty("hello", "Hi"));
476+
initializer.initialize(context);
477+
});
478+
assertThat(freshApplicationContext.getBean("text", String.class)).isEqualTo("Hi World");
479+
});
480+
}
481+
482+
@Test
483+
void processAheadOfTimeWhenHasCglibProxyAndMixedAutowiring() {
484+
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
485+
applicationContext.registerBean(AutowiredMixedCglibConfiguration.class);
486+
testCompiledResult(applicationContext, (initializer, compiled) -> {
487+
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(context -> {
488+
context.setEnvironment(new MockEnvironment().withProperty("hello", "Hi")
489+
.withProperty("world", "AOT World"));
490+
initializer.initialize(context);
491+
});
492+
assertThat(freshApplicationContext.getBean("text", String.class)).isEqualTo("Hi AOT World");
493+
});
494+
}
495+
467496
@Test
468497
void processAheadOfTimeWhenHasCglibProxyWithArgumentsUseProxy() {
469498
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.testfixture.context.annotation;
18+
19+
import org.springframework.beans.factory.annotation.Autowired;
20+
import org.springframework.context.annotation.Bean;
21+
import org.springframework.context.annotation.Configuration;
22+
import org.springframework.core.env.Environment;
23+
24+
@Configuration
25+
public class AutowiredCglibConfiguration {
26+
27+
@Autowired
28+
private Environment environment;
29+
30+
@Bean
31+
public String text() {
32+
return this.environment.getProperty("hello") + " World";
33+
}
34+
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.testfixture.context.annotation;
18+
19+
import org.springframework.beans.factory.annotation.Value;
20+
import org.springframework.context.annotation.Bean;
21+
import org.springframework.context.annotation.Configuration;
22+
import org.springframework.core.env.Environment;
23+
24+
@Configuration
25+
public class AutowiredMixedCglibConfiguration {
26+
27+
@Value("${world:World}")
28+
private String world;
29+
30+
private final Environment environment;
31+
32+
public AutowiredMixedCglibConfiguration(Environment environment) {
33+
this.environment = environment;
34+
}
35+
36+
@Bean
37+
public String text() {
38+
return this.environment.getProperty("hello") + " " + this.world;
39+
}
40+
41+
}

0 commit comments

Comments
 (0)