Skip to content

Commit 086d708

Browse files
committed
Merge branch '6.2.x'
2 parents db19632 + 81a9f3d commit 086d708

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,90 +156,95 @@ 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().registerType(constructor.getDeclaringClass());
176178

177179
if (constructor.getParameterCount() == 0) {
178180
if (!this.allowDirectSupplierShortcut) {
179-
return CodeBlock.of("$T.using($T::new)", InstanceSupplier.class, constructor.getDeclaringClass());
181+
return CodeBlock.of("$T.using($T::new)", InstanceSupplier.class, descriptor.actualType());
180182
}
181183
if (!isThrowingCheckedException(constructor)) {
182-
return CodeBlock.of("$T::new", constructor.getDeclaringClass());
184+
return CodeBlock.of("$T::new", descriptor.actualType());
183185
}
184-
return CodeBlock.of("$T.of($T::new)", ThrowingSupplier.class, constructor.getDeclaringClass());
186+
return CodeBlock.of("$T.of($T::new)", ThrowingSupplier.class, descriptor.actualType());
185187
}
186188

187189
GeneratedMethod generatedMethod = generateGetInstanceSupplierMethod(method ->
188-
buildGetInstanceMethodForConstructor(method, beanName, constructor, PRIVATE_STATIC));
190+
buildGetInstanceMethodForConstructor(method, descriptor, PRIVATE_STATIC));
189191
return generateReturnStatement(generatedMethod);
190192
}
191193

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

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

200203
GeneratedMethod generatedMethod = generateGetInstanceSupplierMethod(method -> {
201-
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
204+
method.addJavadoc("Get the bean instance supplier for '$L'.", descriptor.beanName());
202205
method.addModifiers(PRIVATE_STATIC);
203206
codeWarnings.suppress(method);
204-
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, constructor.getDeclaringClass()));
205-
method.addStatement(generateResolverForConstructor(constructor));
207+
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, descriptor.publicType()));
208+
method.addStatement(generateResolverForConstructor(descriptor));
206209
});
207210

208211
return generateReturnStatement(generatedMethod);
209212
}
210213

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

214-
Class<?> declaringClass = constructor.getDeclaringClass();
217+
Constructor<?> constructor = descriptor.constructor();
218+
Class<?> publicType = descriptor.publicType();
219+
Class<?> actualType = descriptor.actualType();
215220

216221
CodeWarnings codeWarnings = new CodeWarnings();
217-
codeWarnings.detectDeprecation(declaringClass, constructor)
222+
codeWarnings.detectDeprecation(actualType, constructor)
218223
.detectDeprecation(Arrays.stream(constructor.getParameters()).map(Parameter::getType));
219-
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
224+
method.addJavadoc("Get the bean instance supplier for '$L'.", descriptor.beanName());
220225
method.addModifiers(modifiers);
221226
codeWarnings.suppress(method);
222-
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, declaringClass));
227+
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, publicType));
223228

224229
CodeBlock.Builder code = CodeBlock.builder();
225-
code.add(generateResolverForConstructor(constructor));
230+
code.add(generateResolverForConstructor(descriptor));
226231
boolean hasArguments = constructor.getParameterCount() > 0;
227-
boolean onInnerClass = ClassUtils.isInnerClass(declaringClass);
232+
boolean onInnerClass = ClassUtils.isInnerClass(actualType);
228233

229234
CodeBlock arguments = hasArguments ?
230-
new AutowiredArgumentsCodeGenerator(declaringClass, constructor)
235+
new AutowiredArgumentsCodeGenerator(actualType, constructor)
231236
.generateCode(constructor.getParameterTypes(), (onInnerClass ? 1 : 0))
232237
: NO_ARGS;
233238

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

239-
private CodeBlock generateResolverForConstructor(Constructor<?> constructor) {
240-
CodeBlock parameterTypes = generateParameterTypesCode(constructor.getParameterTypes());
244+
private CodeBlock generateResolverForConstructor(ConstructorDescriptor descriptor) {
245+
CodeBlock parameterTypes = generateParameterTypesCode(descriptor.constructor().getParameterTypes());
241246
return CodeBlock.of("return $T.<$T>forConstructor($L)", BeanInstanceSupplier.class,
242-
constructor.getDeclaringClass(), parameterTypes);
247+
descriptor.publicType(), parameterTypes);
243248
}
244249

245250
private CodeBlock generateNewInstanceCodeForConstructor(Class<?> declaringClass, CodeBlock args) {
@@ -437,4 +442,11 @@ private void registerProxyIfNecessary(RuntimeHints runtimeHints, DependencyDescr
437442
}
438443
}
439444

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

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)