40
40
import org .springframework .beans .factory .config .DependencyDescriptor ;
41
41
import org .springframework .beans .factory .support .AbstractAutowireCapableBeanFactory ;
42
42
import org .springframework .beans .factory .support .BeanDefinitionValueResolver ;
43
+ import org .springframework .beans .factory .support .InstanceSupplier ;
43
44
import org .springframework .beans .factory .support .RegisteredBean ;
44
45
import org .springframework .beans .factory .support .RootBeanDefinition ;
45
46
import org .springframework .core .CollectionFactory ;
49
50
import org .springframework .util .ClassUtils ;
50
51
import org .springframework .util .ObjectUtils ;
51
52
import org .springframework .util .ReflectionUtils ;
53
+ import org .springframework .util .function .ThrowingBiFunction ;
52
54
import org .springframework .util .function .ThrowingFunction ;
55
+ import org .springframework .util .function .ThrowingSupplier ;
53
56
54
57
/**
55
- * Resolver used to support the autowiring of constructors or factory methods.
56
- * Typically used in AOT-processed applications as a targeted alternative to the
57
- * reflection based injection.
58
+ * Specialized {@link InstanceSupplier} that provides the factory {@link Method}
59
+ * used to instantiate the underlying bean instance, if any. Transparently
60
+ * handles resolution of {@link AutowiredArguments} if necessary. Typically used
61
+ * in AOT-processed applications as a targeted alternative to the reflection
62
+ * based injection.
58
63
* <p>
59
- * When resolving arguments in a native image, the {@link Constructor} or
60
- * {@link Method} being used must be marked with an
61
- * {@link ExecutableMode#INTROSPECT introspection} hint so that parameter
62
- * annotations can be read. Full {@link ExecutableMode#INVOKE invocation} hints
63
- * are only required if the {@code resolveAndInstantiate} methods of this class
64
- * are being used (typically to support private constructors, methods or
65
- * classes).
64
+ * If no {@code generator} is provided, reflection is used to instantiate the
65
+ * bean instance, and full {@link ExecutableMode#INVOKE invocation} hints are
66
+ * contributed. Multiple generator callback styles are supported:
67
+ * <ul>
68
+ * <li>A function with the {@code registeredBean} and resolved {@code arguments}
69
+ * for executables that require arguments resolution. An
70
+ * {@link ExecutableMode#INTROSPECT introspection} hint is added so that
71
+ * parameter annotations can be read </li>
72
+ * <li>A function with only the {@code registeredBean} for simpler cases that
73
+ * do not require resolution of arguments</li>
74
+ * <li>A supplier when a method reference can be used</li>
75
+ * </ul>
76
+ * Generator callbacks handle checked exceptions so that the caller does not
77
+ * have to deal with it.
66
78
*
67
79
* @author Phillip Webb
68
80
* @author Stephane Nicoll
69
81
* @since 6.0
70
82
* @see AutowiredArguments
71
83
*/
72
- public final class AutowiredInstantiationArgumentsResolver extends AutowiredElementResolver {
84
+ public final class BeanInstanceSupplier extends AutowiredElementResolver implements InstanceSupplier < Object > {
73
85
74
86
private final ExecutableLookup lookup ;
75
87
88
+ @ Nullable
89
+ private final ThrowingBiFunction <RegisteredBean , AutowiredArguments , Object > generator ;
90
+
76
91
@ Nullable
77
92
private final String [] shortcuts ;
78
93
79
94
80
- private AutowiredInstantiationArgumentsResolver (ExecutableLookup lookup ,
95
+ private BeanInstanceSupplier (ExecutableLookup lookup ,
96
+ @ Nullable ThrowingBiFunction <RegisteredBean , AutowiredArguments , Object > generator ,
81
97
@ Nullable String [] shortcuts ) {
82
-
83
98
this .lookup = lookup ;
99
+ this .generator = generator ;
84
100
this .shortcuts = shortcuts ;
85
101
}
86
102
87
-
88
103
/**
89
- * Create a {@link AutowiredInstantiationArgumentsResolver } that resolves
104
+ * Create a {@link BeanInstanceSupplier } that resolves
90
105
* arguments for the specified bean constructor.
91
106
* @param parameterTypes the constructor parameter types
92
- * @return a new {@link AutowiredInstantiationArgumentsResolver } instance
107
+ * @return a new {@link BeanInstanceSupplier } instance
93
108
*/
94
- public static AutowiredInstantiationArgumentsResolver forConstructor (
109
+ public static BeanInstanceSupplier forConstructor (
95
110
Class <?>... parameterTypes ) {
96
111
97
112
Assert .notNull (parameterTypes , "'parameterTypes' must not be null" );
98
113
Assert .noNullElements (parameterTypes ,
99
114
"'parameterTypes' must not contain null elements" );
100
- return new AutowiredInstantiationArgumentsResolver (
101
- new ConstructorLookup (parameterTypes ), null );
115
+ return new BeanInstanceSupplier (
116
+ new ConstructorLookup (parameterTypes ), null , null );
102
117
}
103
118
104
119
/**
105
- * Create a new {@link AutowiredInstantiationArgumentsResolver } that
120
+ * Create a new {@link BeanInstanceSupplier } that
106
121
* resolves arguments for the specified factory method.
107
122
* @param declaringClass the class that declares the factory method
108
123
* @param methodName the factory method name
109
124
* @param parameterTypes the factory method parameter types
110
- * @return a new {@link AutowiredInstantiationArgumentsResolver } instance
125
+ * @return a new {@link BeanInstanceSupplier } instance
111
126
*/
112
- public static AutowiredInstantiationArgumentsResolver forFactoryMethod (
127
+ public static BeanInstanceSupplier forFactoryMethod (
113
128
Class <?> declaringClass , String methodName , Class <?>... parameterTypes ) {
114
129
115
130
Assert .notNull (declaringClass , "'declaringClass' must not be null" );
116
131
Assert .hasText (methodName , "'methodName' must not be empty" );
117
132
Assert .notNull (parameterTypes , "'parameterTypes' must not be null" );
118
133
Assert .noNullElements (parameterTypes ,
119
134
"'parameterTypes' must not contain null elements" );
120
- return new AutowiredInstantiationArgumentsResolver (
135
+ return new BeanInstanceSupplier (
121
136
new FactoryMethodLookup (declaringClass , methodName , parameterTypes ),
122
- null );
137
+ null , null );
123
138
}
124
139
125
140
@@ -128,74 +143,92 @@ ExecutableLookup getLookup() {
128
143
}
129
144
130
145
/**
131
- * Return a new {@link AutowiredInstantiationArgumentsResolver} instance
132
- * that uses direct bean name injection shortcuts for specific parameters.
133
- * @param beanNames the bean names to use as shortcuts (aligned with the
134
- * constructor or factory method parameters)
135
- * @return a new {@link AutowiredInstantiationArgumentsResolver} instance
136
- * that uses the shortcuts
146
+ * Return a new {@link BeanInstanceSupplier} instance that uses the specified
147
+ * {@code generator} bi-function to instantiate the underlying bean.
148
+ * @param generator a {@link ThrowingBiFunction} that uses the
149
+ * {@link RegisteredBean} and resolved {@link AutowiredArguments} to
150
+ * instantiate the underlying bean
151
+ * @return a new {@link BeanInstanceSupplier} instance with the specified
152
+ * generator
137
153
*/
138
- public AutowiredInstantiationArgumentsResolver withShortcuts (String ... beanNames ) {
139
- return new AutowiredInstantiationArgumentsResolver (this .lookup , beanNames );
154
+ public BeanInstanceSupplier withGenerator (
155
+ ThrowingBiFunction <RegisteredBean , AutowiredArguments , Object > generator ) {
156
+ Assert .notNull (generator , "'generator' must not be null" );
157
+ return new BeanInstanceSupplier (this .lookup , generator , this .shortcuts );
140
158
}
141
159
142
160
/**
143
- * Resolve arguments for the specified registered bean and provide them to
144
- * the given generator in order to return a result.
145
- * @param registeredBean the registered bean
146
- * @param generator the generator to execute with the resolved constructor
147
- * or factory method arguments
161
+ * Return a new {@link BeanInstanceSupplier} instance that uses the specified
162
+ * {@code generator} function to instantiate the underlying bean.
163
+ * @param generator a {@link ThrowingFunction} that uses the
164
+ * {@link RegisteredBean} to instantiate the underlying bean
165
+ * @return a new {@link BeanInstanceSupplier} instance with the specified
166
+ * generator
148
167
*/
149
- public <T > T resolve (RegisteredBean registeredBean ,
150
- ThrowingFunction <AutowiredArguments , T > generator ) {
151
-
152
- Assert .notNull (registeredBean , "'registeredBean' must not be null" );
153
- Assert .notNull (generator , "'action' must not be null" );
154
- AutowiredArguments resolved = resolveArguments (registeredBean ,
155
- this .lookup .get (registeredBean ));
156
- return generator .apply (resolved );
168
+ public BeanInstanceSupplier withGenerator (
169
+ ThrowingFunction <RegisteredBean , Object > generator ) {
170
+ Assert .notNull (generator , "'generator' must not be null" );
171
+ return new BeanInstanceSupplier (this .lookup , (registeredBean , args ) ->
172
+ generator .apply (registeredBean ), this .shortcuts );
157
173
}
158
174
159
175
/**
160
- * Resolve arguments for the specified registered bean.
161
- * @param registeredBean the registered bean
162
- * @return the resolved constructor or factory method arguments
176
+ * Return a new {@link BeanInstanceSupplier} instance that uses the specified
177
+ * {@code generator} supplier to instantiate the underlying bean.
178
+ * @param generator a {@link ThrowingSupplier} to instantiate the underlying
179
+ * bean
180
+ * @return a new {@link BeanInstanceSupplier} instance with the specified
181
+ * generator
163
182
*/
164
- public AutowiredArguments resolve (RegisteredBean registeredBean ) {
165
- Assert .notNull (registeredBean , "'registeredBean' must not be null" );
166
- return resolveArguments (registeredBean , this .lookup .get (registeredBean ));
183
+ public BeanInstanceSupplier withGenerator (ThrowingSupplier <Object > generator ) {
184
+ Assert .notNull (generator , "'generator' must not be null" );
185
+ return new BeanInstanceSupplier (this .lookup , (registeredBean , args ) ->
186
+ generator .get (), this .shortcuts );
167
187
}
168
188
169
189
/**
170
- * Resolve arguments for the specified registered bean and instantiate a new
171
- * instance using reflection.
172
- * @param registeredBean the registered bean
173
- * @return an instance of the bean
190
+ * Return a new {@link BeanInstanceSupplier} instance
191
+ * that uses direct bean name injection shortcuts for specific parameters.
192
+ * @param beanNames the bean names to use as shortcuts (aligned with the
193
+ * constructor or factory method parameters)
194
+ * @return a new {@link BeanInstanceSupplier} instance
195
+ * that uses the shortcuts
174
196
*/
175
- @ SuppressWarnings ("unchecked" )
176
- public <T > T resolveAndInstantiate (RegisteredBean registeredBean ) {
177
- return (T ) resolveAndInstantiate (registeredBean , Object .class );
197
+ public BeanInstanceSupplier withShortcuts (String ... beanNames ) {
198
+ return new BeanInstanceSupplier (this .lookup , this .generator , beanNames );
199
+ }
200
+
201
+ @ Override
202
+ public Object get (RegisteredBean registeredBean ) throws Exception {
203
+ Assert .notNull (registeredBean , "'registeredBean' must not be null" );
204
+ Executable executable = this .lookup .get (registeredBean );
205
+ AutowiredArguments arguments = resolveArguments (registeredBean , executable );
206
+ if (this .generator != null ) {
207
+ return this .generator .apply (registeredBean , arguments );
208
+ }
209
+ else {
210
+ return instantiate (registeredBean .getBeanFactory (), executable ,
211
+ arguments .toArray ());
212
+ }
213
+ }
214
+
215
+ @ Nullable
216
+ @ Override
217
+ public Method getFactoryMethod () {
218
+ if (this .lookup instanceof FactoryMethodLookup factoryMethodLookup ) {
219
+ return factoryMethodLookup .get ();
220
+ }
221
+ return null ;
178
222
}
179
223
180
224
/**
181
- * Resolve arguments for the specified registered bean and instantiate a new
182
- * instance using reflection.
225
+ * Resolve arguments for the specified registered bean.
183
226
* @param registeredBean the registered bean
184
- * @param requiredType the required result type
185
- * @return an instance of the bean
227
+ * @return the resolved constructor or factory method arguments
186
228
*/
187
- @ SuppressWarnings ("unchecked" )
188
- public <T > T resolveAndInstantiate (RegisteredBean registeredBean ,
189
- Class <T > requiredType ) {
190
-
229
+ AutowiredArguments resolveArguments (RegisteredBean registeredBean ) {
191
230
Assert .notNull (registeredBean , "'registeredBean' must not be null" );
192
- Assert .notNull (registeredBean , "'requiredType' must not be null" );
193
- Executable executable = this .lookup .get (registeredBean );
194
- AutowiredArguments arguments = resolveArguments (registeredBean , executable );
195
- Object instance = instantiate (registeredBean .getBeanFactory (), executable ,
196
- arguments .toArray ());
197
- Assert .isInstanceOf (requiredType , instance );
198
- return (T ) instance ;
231
+ return resolveArguments (registeredBean , this .lookup .get (registeredBean ));
199
232
}
200
233
201
234
private AutowiredArguments resolveArguments (RegisteredBean registeredBean ,
@@ -233,9 +266,6 @@ private AutowiredArguments resolveArguments(RegisteredBean registeredBean,
233
266
autowiredBeans , parameter , dependencyDescriptor , argumentValue );
234
267
}
235
268
registerDependentBeans (beanFactory , beanName , autowiredBeans );
236
- if (executable instanceof Method method ) {
237
- mergedBeanDefinition .setResolvedFactoryMethod (method );
238
- }
239
269
return AutowiredArguments .of (resolved );
240
270
}
241
271
@@ -403,10 +433,12 @@ private static class ConstructorLookup extends ExecutableLookup {
403
433
404
434
private final Class <?>[] parameterTypes ;
405
435
436
+
406
437
ConstructorLookup (Class <?>[] parameterTypes ) {
407
438
this .parameterTypes = parameterTypes ;
408
439
}
409
440
441
+
410
442
@ Override
411
443
public Executable get (RegisteredBean registeredBean ) {
412
444
Class <?> beanClass = registeredBean .getBeanClass ();
@@ -453,6 +485,10 @@ private static class FactoryMethodLookup extends ExecutableLookup {
453
485
454
486
@ Override
455
487
public Executable get (RegisteredBean registeredBean ) {
488
+ return get ();
489
+ }
490
+
491
+ Method get () {
456
492
Method method = ReflectionUtils .findMethod (this .declaringClass ,
457
493
this .methodName , this .parameterTypes );
458
494
Assert .notNull (method , () -> String .format ("%s cannot be found" , this ));
0 commit comments