Skip to content

Commit cfb61d2

Browse files
committed
Add generics to BeanInstanceSupplier
Update `BeanInstanceSupplier` to support a generic type. See gh-28748
1 parent 069d6d3 commit cfb61d2

File tree

7 files changed

+167
-94
lines changed

7 files changed

+167
-94
lines changed

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

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -79,21 +79,22 @@
7979
* @author Phillip Webb
8080
* @author Stephane Nicoll
8181
* @since 6.0
82+
* @param <T> the type of instance supplied by this supplier
8283
* @see AutowiredArguments
8384
*/
84-
public final class BeanInstanceSupplier extends AutowiredElementResolver implements InstanceSupplier<Object> {
85+
public final class BeanInstanceSupplier<T> extends AutowiredElementResolver implements InstanceSupplier<T> {
8586

8687
private final ExecutableLookup lookup;
8788

8889
@Nullable
89-
private final ThrowingBiFunction<RegisteredBean, AutowiredArguments, Object> generator;
90+
private final ThrowingBiFunction<RegisteredBean, AutowiredArguments, T> generator;
9091

9192
@Nullable
9293
private final String[] shortcuts;
9394

9495

9596
private BeanInstanceSupplier(ExecutableLookup lookup,
96-
@Nullable ThrowingBiFunction<RegisteredBean, AutowiredArguments, Object> generator,
97+
@Nullable ThrowingBiFunction<RegisteredBean, AutowiredArguments, T> generator,
9798
@Nullable String[] shortcuts) {
9899
this.lookup = lookup;
99100
this.generator = generator;
@@ -103,36 +104,38 @@ private BeanInstanceSupplier(ExecutableLookup lookup,
103104
/**
104105
* Create a {@link BeanInstanceSupplier} that resolves
105106
* arguments for the specified bean constructor.
107+
* @param <T> the type of instance supplied
106108
* @param parameterTypes the constructor parameter types
107109
* @return a new {@link BeanInstanceSupplier} instance
108110
*/
109-
public static BeanInstanceSupplier forConstructor(
111+
public static <T> BeanInstanceSupplier<T> forConstructor(
110112
Class<?>... parameterTypes) {
111113

112114
Assert.notNull(parameterTypes, "'parameterTypes' must not be null");
113115
Assert.noNullElements(parameterTypes,
114116
"'parameterTypes' must not contain null elements");
115-
return new BeanInstanceSupplier(
117+
return new BeanInstanceSupplier<>(
116118
new ConstructorLookup(parameterTypes), null, null);
117119
}
118120

119121
/**
120122
* Create a new {@link BeanInstanceSupplier} that
121123
* resolves arguments for the specified factory method.
124+
* @param <T> the type of instance supplied
122125
* @param declaringClass the class that declares the factory method
123126
* @param methodName the factory method name
124127
* @param parameterTypes the factory method parameter types
125128
* @return a new {@link BeanInstanceSupplier} instance
126129
*/
127-
public static BeanInstanceSupplier forFactoryMethod(
130+
public static <T> BeanInstanceSupplier<T> forFactoryMethod(
128131
Class<?> declaringClass, String methodName, Class<?>... parameterTypes) {
129132

130133
Assert.notNull(declaringClass, "'declaringClass' must not be null");
131134
Assert.hasText(methodName, "'methodName' must not be empty");
132135
Assert.notNull(parameterTypes, "'parameterTypes' must not be null");
133136
Assert.noNullElements(parameterTypes,
134137
"'parameterTypes' must not contain null elements");
135-
return new BeanInstanceSupplier(
138+
return new BeanInstanceSupplier<>(
136139
new FactoryMethodLookup(declaringClass, methodName, parameterTypes),
137140
null, null);
138141
}
@@ -151,10 +154,10 @@ ExecutableLookup getLookup() {
151154
* @return a new {@link BeanInstanceSupplier} instance with the specified
152155
* generator
153156
*/
154-
public BeanInstanceSupplier withGenerator(
155-
ThrowingBiFunction<RegisteredBean, AutowiredArguments, Object> generator) {
157+
public BeanInstanceSupplier<T> withGenerator(
158+
ThrowingBiFunction<RegisteredBean, AutowiredArguments, T> generator) {
156159
Assert.notNull(generator, "'generator' must not be null");
157-
return new BeanInstanceSupplier(this.lookup, generator, this.shortcuts);
160+
return new BeanInstanceSupplier<T>(this.lookup, generator, this.shortcuts);
158161
}
159162

160163
/**
@@ -165,10 +168,10 @@ public BeanInstanceSupplier withGenerator(
165168
* @return a new {@link BeanInstanceSupplier} instance with the specified
166169
* generator
167170
*/
168-
public BeanInstanceSupplier withGenerator(
169-
ThrowingFunction<RegisteredBean, Object> generator) {
171+
public BeanInstanceSupplier<T> withGenerator(
172+
ThrowingFunction<RegisteredBean, T> generator) {
170173
Assert.notNull(generator, "'generator' must not be null");
171-
return new BeanInstanceSupplier(this.lookup, (registeredBean, args) ->
174+
return new BeanInstanceSupplier<>(this.lookup, (registeredBean, args) ->
172175
generator.apply(registeredBean), this.shortcuts);
173176
}
174177

@@ -180,9 +183,9 @@ public BeanInstanceSupplier withGenerator(
180183
* @return a new {@link BeanInstanceSupplier} instance with the specified
181184
* generator
182185
*/
183-
public BeanInstanceSupplier withGenerator(ThrowingSupplier<Object> generator) {
186+
public BeanInstanceSupplier<T> withGenerator(ThrowingSupplier<T> generator) {
184187
Assert.notNull(generator, "'generator' must not be null");
185-
return new BeanInstanceSupplier(this.lookup, (registeredBean, args) ->
188+
return new BeanInstanceSupplier<>(this.lookup, (registeredBean, args) ->
186189
generator.get(), this.shortcuts);
187190
}
188191

@@ -194,22 +197,19 @@ public BeanInstanceSupplier withGenerator(ThrowingSupplier<Object> generator) {
194197
* @return a new {@link BeanInstanceSupplier} instance
195198
* that uses the shortcuts
196199
*/
197-
public BeanInstanceSupplier withShortcuts(String... beanNames) {
198-
return new BeanInstanceSupplier(this.lookup, this.generator, beanNames);
200+
public BeanInstanceSupplier<T> withShortcuts(String... beanNames) {
201+
return new BeanInstanceSupplier<T>(this.lookup, this.generator, beanNames);
199202
}
200203

201204
@Override
202-
public Object get(RegisteredBean registeredBean) throws Exception {
205+
public T get(RegisteredBean registeredBean) throws Exception {
203206
Assert.notNull(registeredBean, "'registeredBean' must not be null");
204207
Executable executable = this.lookup.get(registeredBean);
205208
AutowiredArguments arguments = resolveArguments(registeredBean, executable);
206209
if (this.generator != null) {
207210
return this.generator.apply(registeredBean, arguments);
208211
}
209-
else {
210-
return instantiate(registeredBean.getBeanFactory(), executable,
211-
arguments.toArray());
212-
}
212+
return instantiate(registeredBean.getBeanFactory(), executable, arguments.toArray());
213213
}
214214

215215
@Nullable
@@ -351,15 +351,16 @@ private Object resolveArgument(AbstractAutowireCapableBeanFactory beanFactory,
351351
}
352352
}
353353

354-
private Object instantiate(ConfigurableBeanFactory beanFactory, Executable executable,
354+
@SuppressWarnings("unchecked")
355+
private T instantiate(ConfigurableBeanFactory beanFactory, Executable executable,
355356
Object[] arguments) {
356357

357358
try {
358359
if (executable instanceof Constructor<?> constructor) {
359-
return instantiate(constructor, arguments);
360+
return (T) instantiate(constructor, arguments);
360361
}
361362
if (executable instanceof Method method) {
362-
return instantiate(beanFactory, method, arguments);
363+
return (T) instantiate(beanFactory, method, arguments);
363364
}
364365
}
365366
catch (Exception ex) {

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,8 @@
4141
class BeanRegistrationsAotContribution
4242
implements BeanFactoryInitializationAotContribution {
4343

44-
4544
private static final String BEAN_FACTORY_PARAMETER_NAME = "beanFactory";
4645

47-
4846
private final Map<String, BeanDefinitionMethodGenerator> registrations;
4947

5048

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

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.javapoet.CodeBlock;
3838
import org.springframework.javapoet.CodeBlock.Builder;
3939
import org.springframework.javapoet.MethodSpec;
40+
import org.springframework.javapoet.ParameterizedTypeName;
4041
import org.springframework.util.ClassUtils;
4142
import org.springframework.util.function.ThrowingSupplier;
4243

@@ -108,21 +109,22 @@ CodeBlock generateCode(RegisteredBean registeredBean,
108109
private CodeBlock generateCodeForConstructor(RegisteredBean registeredBean,
109110
Constructor<?> constructor) {
110111

111-
String name = registeredBean.getBeanName();
112+
String beanName = registeredBean.getBeanName();
113+
Class<?> beanClass = registeredBean.getBeanClass();
112114
Class<?> declaringClass = ClassUtils
113115
.getUserClass(constructor.getDeclaringClass());
114116
boolean dependsOnBean = ClassUtils.isInnerClass(declaringClass);
115117
AccessVisibility accessVisibility = getAccessVisibility(registeredBean,
116118
constructor);
117119
if (accessVisibility == AccessVisibility.PUBLIC
118120
|| accessVisibility == AccessVisibility.PACKAGE_PRIVATE) {
119-
return generateCodeForAccessibleConstructor(name, constructor, dependsOnBean,
120-
declaringClass);
121+
return generateCodeForAccessibleConstructor(beanName, beanClass, constructor,
122+
dependsOnBean, declaringClass);
121123
}
122-
return generateCodeForInaccessibleConstructor(name, constructor, dependsOnBean);
124+
return generateCodeForInaccessibleConstructor(beanName, beanClass, constructor, dependsOnBean);
123125
}
124126

125-
private CodeBlock generateCodeForAccessibleConstructor(String name,
127+
private CodeBlock generateCodeForAccessibleConstructor(String beanName, Class<?> beanClass,
126128
Constructor<?> constructor, boolean dependsOnBean, Class<?> declaringClass) {
127129

128130
this.generationContext.getRuntimeHints().reflection()
@@ -139,37 +141,36 @@ private CodeBlock generateCodeForAccessibleConstructor(String name,
139141
declaringClass);
140142
}
141143
GeneratedMethod generatedMethod = generateGetInstanceSupplierMethod(method ->
142-
buildGetInstanceMethodForConstructor(method, name, constructor,
144+
buildGetInstanceMethodForConstructor(method, beanName, beanClass, constructor,
143145
declaringClass, dependsOnBean, PRIVATE_STATIC));
144146
return generateReturnStatement(generatedMethod);
145147
}
146148

147-
private CodeBlock generateCodeForInaccessibleConstructor(String name,
148-
Constructor<?> constructor, boolean dependsOnBean) {
149+
private CodeBlock generateCodeForInaccessibleConstructor(String beanName,
150+
Class<?> beanClass, Constructor<?> constructor, boolean dependsOnBean) {
149151

150152
this.generationContext.getRuntimeHints().reflection()
151153
.registerConstructor(constructor);
152154
GeneratedMethod generatedMethod = generateGetInstanceSupplierMethod(method -> {
153-
method.addJavadoc("Get the bean instance supplier for '$L'.", name);
155+
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
154156
method.addModifiers(PRIVATE_STATIC);
155-
method.returns(BeanInstanceSupplier.class);
157+
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
156158
int parameterOffset = (!dependsOnBean) ? 0 : 1;
157-
method.addStatement(
158-
generateResolverForConstructor(constructor, parameterOffset));
159+
method.addStatement(generateResolverForConstructor(beanClass, constructor, parameterOffset));
159160
});
160161
return generateReturnStatement(generatedMethod);
161162
}
162163

163164
private void buildGetInstanceMethodForConstructor(MethodSpec.Builder method,
164-
String name, Constructor<?> constructor, Class<?> declaringClass,
165+
String beanName, Class<?> beanClass, Constructor<?> constructor, Class<?> declaringClass,
165166
boolean dependsOnBean, javax.lang.model.element.Modifier... modifiers) {
166167

167-
method.addJavadoc("Get the bean instance supplier for '$L'.", name);
168+
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
168169
method.addModifiers(modifiers);
169-
method.returns(BeanInstanceSupplier.class);
170+
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
170171
int parameterOffset = (!dependsOnBean) ? 0 : 1;
171172
CodeBlock.Builder code = CodeBlock.builder();
172-
code.add(generateResolverForConstructor(constructor, parameterOffset));
173+
code.add(generateResolverForConstructor(beanClass, constructor, parameterOffset));
173174
boolean hasArguments = constructor.getParameterCount() > 0;
174175
CodeBlock arguments = hasArguments
175176
? new AutowiredArgumentsCodeGenerator(declaringClass, constructor)
@@ -181,13 +182,13 @@ private void buildGetInstanceMethodForConstructor(MethodSpec.Builder method,
181182
method.addStatement(code.build());
182183
}
183184

184-
private CodeBlock generateResolverForConstructor(Constructor<?> constructor,
185-
int parameterOffset) {
185+
private CodeBlock generateResolverForConstructor(Class<?> beanClass,
186+
Constructor<?> constructor, int parameterOffset) {
186187

187188
CodeBlock parameterTypes = generateParameterTypesCode(
188189
constructor.getParameterTypes(), parameterOffset);
189-
return CodeBlock.of("return $T.forConstructor($L)",
190-
BeanInstanceSupplier.class, parameterTypes);
190+
return CodeBlock.of("return $T.<$T>forConstructor($L)",
191+
BeanInstanceSupplier.class, beanClass, parameterTypes);
191192
}
192193

193194
private CodeBlock generateNewInstanceCodeForConstructor(boolean dependsOnBean,
@@ -204,63 +205,65 @@ private CodeBlock generateNewInstanceCodeForConstructor(boolean dependsOnBean,
204205
private CodeBlock generateCodeForFactoryMethod(RegisteredBean registeredBean,
205206
Method factoryMethod) {
206207

207-
String name = registeredBean.getBeanName();
208+
String beanName = registeredBean.getBeanName();
209+
Class<?> beanClass = registeredBean.getBeanClass();
208210
Class<?> declaringClass = ClassUtils
209211
.getUserClass(factoryMethod.getDeclaringClass());
210212
boolean dependsOnBean = !Modifier.isStatic(factoryMethod.getModifiers());
211213
AccessVisibility accessVisibility = getAccessVisibility(registeredBean,
212214
factoryMethod);
213215
if (accessVisibility == AccessVisibility.PUBLIC
214216
|| accessVisibility == AccessVisibility.PACKAGE_PRIVATE) {
215-
return generateCodeForAccessibleFactoryMethod(name, factoryMethod,
217+
return generateCodeForAccessibleFactoryMethod(beanName, beanClass, factoryMethod,
216218
declaringClass, dependsOnBean);
217219
}
218-
return generateCodeForInaccessibleFactoryMethod(name, factoryMethod,
220+
return generateCodeForInaccessibleFactoryMethod(beanName, beanClass, factoryMethod,
219221
declaringClass);
220222
}
221223

222-
private CodeBlock generateCodeForAccessibleFactoryMethod(String name,
223-
Method factoryMethod, Class<?> declaringClass, boolean dependsOnBean) {
224+
private CodeBlock generateCodeForAccessibleFactoryMethod(String beanName,
225+
Class<?> beanClass, Method factoryMethod, Class<?> declaringClass, boolean dependsOnBean) {
226+
224227
this.generationContext.getRuntimeHints().reflection()
225228
.registerMethod(factoryMethod, INTROSPECT);
226229
if (!dependsOnBean && factoryMethod.getParameterCount() == 0) {
227230
CodeBlock.Builder code = CodeBlock.builder();
228-
code.add("$T.forFactoryMethod($T.class, $S)", BeanInstanceSupplier.class,
229-
declaringClass, factoryMethod.getName());
231+
code.add("$T.<$T>forFactoryMethod($T.class, $S)", BeanInstanceSupplier.class,
232+
beanClass, declaringClass, factoryMethod.getName());
230233
code.add(".withGenerator($T::$L)", declaringClass, factoryMethod.getName());
231234
return code.build();
232235
}
233236
GeneratedMethod getInstanceMethod = generateGetInstanceSupplierMethod(method ->
234-
buildGetInstanceMethodForFactoryMethod(method, name, factoryMethod,
237+
buildGetInstanceMethodForFactoryMethod(method, beanName, beanClass, factoryMethod,
235238
declaringClass, dependsOnBean, PRIVATE_STATIC));
236239
return generateReturnStatement(getInstanceMethod);
237240
}
238241

239-
private CodeBlock generateCodeForInaccessibleFactoryMethod(String name,
242+
private CodeBlock generateCodeForInaccessibleFactoryMethod(String beanName, Class<?> beanClass,
240243
Method factoryMethod, Class<?> declaringClass) {
241244

242245
this.generationContext.getRuntimeHints().reflection()
243246
.registerMethod(factoryMethod);
244247
GeneratedMethod getInstanceMethod = generateGetInstanceSupplierMethod(method -> {
245-
method.addJavadoc("Get the bean instance supplier for '$L'.", name);
248+
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
246249
method.addModifiers(PRIVATE_STATIC);
247-
method.returns(BeanInstanceSupplier.class);
248-
method.addStatement(generateInstanceSupplierForFactoryMethod(factoryMethod,
250+
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
251+
method.addStatement(generateInstanceSupplierForFactoryMethod(beanClass, factoryMethod,
249252
declaringClass, factoryMethod.getName()));
250253
});
251254
return generateReturnStatement(getInstanceMethod);
252255
}
253256

254257
private void buildGetInstanceMethodForFactoryMethod(MethodSpec.Builder method,
255-
String name, Method factoryMethod, Class<?> declaringClass,
258+
String beanName, Class<?> beanClass, Method factoryMethod, Class<?> declaringClass,
256259
boolean dependsOnBean, javax.lang.model.element.Modifier... modifiers) {
257260

258261
String factoryMethodName = factoryMethod.getName();
259-
method.addJavadoc("Get the bean instance supplier for '$L'.", name);
262+
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
260263
method.addModifiers(modifiers);
261-
method.returns(BeanInstanceSupplier.class);
264+
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
262265
CodeBlock.Builder code = CodeBlock.builder();
263-
code.add(generateInstanceSupplierForFactoryMethod(factoryMethod, declaringClass, factoryMethodName));
266+
code.add(generateInstanceSupplierForFactoryMethod(beanClass, factoryMethod, declaringClass, factoryMethodName));
264267
boolean hasArguments = factoryMethod.getParameterCount() > 0;
265268
CodeBlock arguments = hasArguments
266269
? new AutowiredArgumentsCodeGenerator(declaringClass, factoryMethod)
@@ -272,18 +275,18 @@ private void buildGetInstanceMethodForFactoryMethod(MethodSpec.Builder method,
272275
method.addStatement(code.build());
273276
}
274277

275-
private CodeBlock generateInstanceSupplierForFactoryMethod(Method factoryMethod,
276-
Class<?> declaringClass, String factoryMethodName) {
278+
private CodeBlock generateInstanceSupplierForFactoryMethod(Class<?> beanClass,
279+
Method factoryMethod, Class<?> declaringClass, String factoryMethodName) {
277280

278281
if (factoryMethod.getParameterCount() == 0) {
279-
return CodeBlock.of("return $T.forFactoryMethod($T.class, $S)",
280-
BeanInstanceSupplier.class, declaringClass,
282+
return CodeBlock.of("return $T.<$T>forFactoryMethod($T.class, $S)",
283+
BeanInstanceSupplier.class, beanClass, declaringClass,
281284
factoryMethodName);
282285
}
283286
CodeBlock parameterTypes = generateParameterTypesCode(
284287
factoryMethod.getParameterTypes(), 0);
285-
return CodeBlock.of("return $T.forFactoryMethod($T.class, $S, $L)",
286-
BeanInstanceSupplier.class, declaringClass,
288+
return CodeBlock.of("return $T.<$T>forFactoryMethod($T.class, $S, $L)",
289+
BeanInstanceSupplier.class, beanClass, declaringClass,
287290
factoryMethodName, parameterTypes);
288291
}
289292

0 commit comments

Comments
 (0)