Skip to content

Commit 93fa010

Browse files
committed
Document AOT best practices
This commit augment the AOT section with best practices we have experienced while working on the AOT engine. In particular, it describes how a FactoryBean with an unresolved generic should be handled. Closes gh-30434
1 parent c1f6d71 commit 93fa010

File tree

1 file changed

+126
-1
lines changed
  • framework-docs/modules/ROOT/pages/core

1 file changed

+126
-1
lines changed

framework-docs/modules/ROOT/pages/core/aot.adoc

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ Applying such optimizations early implies the following restrictions:
1818
** `@Profile`, in particular profile-specific configuration needs to be chosen at build time.
1919
** `Environment` properties that impact the presence of a bean (`@Conditional`) are only considered at build time.
2020
* Bean definitions with instance suppliers (lambdas or method references) cannot be transformed ahead-of-time (see related https://github.com/spring-projects/spring-framework/issues/29555[spring-framework#29555] issue).
21-
* The return type of methods annotated with `@Bean` should be the most specific type possible (typically the concrete class, not an interface) in order to support proper type inference without invoking the corresponding `@Bean` method at build time.
21+
* Make sure that the bean type is as precise as possible.
22+
23+
TIP: See also the xref:core/aot.adoc#aot.bestpractices[] section.
2224

2325
When these restrictions are in place, it becomes possible to perform ahead-of-time processing at build time and generate additional assets.
2426
A Spring AOT processed application typically generates:
@@ -193,6 +195,129 @@ There is a bean definition for `dataSourceConfiguration` and one for `dataSource
193195
When a `datasource` instance is required, a `BeanInstanceSupplier` is called.
194196
This supplier invokes the `dataSource()` method on the `dataSourceConfiguration` bean.
195197

198+
[[aot.bestpractices]]
199+
== Best Practices
200+
201+
The AOT engine is designed to handle as many use cases as possible, with no code change in applications.
202+
However, keep in mind that some optimizations are made at build time based on a static definition of the beans.
203+
204+
This section lists the best practices that make sure your application is ready for AOT.
205+
206+
[[aot.bestpractices.bean-type]]
207+
=== Expose The Most Precise Bean Type
208+
209+
While your application may interact with an interface that a bean implements, it is still very important to declare the most precise type.
210+
The AOT engine performs additional checks on the bean type, such as detecting the presence of `@Autowired` members, or lifecycle callback methods.
211+
212+
For `@Configuration` classes, make sure that the return type of the factory `@Bean` method is as precise as possible.
213+
Consider the following example:
214+
215+
[tabs]
216+
======
217+
Java::
218+
+
219+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
220+
----
221+
@Configuration(proxyBeanMethods = false)
222+
public class UserConfiguration {
223+
224+
@Bean
225+
public MyInterface myInterface() {
226+
return new MyImplementation();
227+
}
228+
229+
}
230+
----
231+
======
232+
233+
In the example above, the declared type for the `myInterface` bean is `MyInterface`.
234+
None of the usual post-processing will take `MyImplementation` into account.
235+
For instance, if there is an annotated handler method on `MyImplementation` that the context should register, it won’t be detected upfront.
236+
237+
The example above should be rewritten as follows:
238+
239+
[tabs]
240+
======
241+
Java::
242+
+
243+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
244+
----
245+
@Configuration(proxyBeanMethods = false)
246+
public class UserConfiguration {
247+
248+
@Bean
249+
public MyImplementation myInterface() {
250+
return new MyImplementation();
251+
}
252+
253+
}
254+
----
255+
======
256+
257+
If you are registering bean definitions programmatically, consider using `RootBeanBefinition` as it allows to specify a `ResolvableType` that handles generics.
258+
259+
[[aot.bestpractices.factory-bean]]
260+
=== FactoryBean
261+
262+
`FactoryBean` should be used with care as it introduces an intermediate layer in terms of bean type resolution that may not be conceptually necessary.
263+
As a rule of thumb, if the `FactoryBean` instance does not hold long-term state and is not needed at a later point in time at runtime, it should be replaced by a regular factory method, possibly with a `FactoryBean` adapter layer on top (for declarative configuration purposes).
264+
265+
If your `FactoryBean` implementation does not resolve the object type (i.e. `T`), extra care is necessary.
266+
Consider the following example:
267+
268+
[tabs]
269+
======
270+
Java::
271+
+
272+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
273+
----
274+
public class ClientFactoryBean<T extends AbstractClient> implements FactoryBean<T> {
275+
276+
}
277+
----
278+
======
279+
280+
A concrete client declaration should provide a resolved generic for the client, as shown in the following example:
281+
282+
[tabs]
283+
======
284+
Java::
285+
+
286+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
287+
----
288+
@Configuration(proxyBeanMethods = false)
289+
public class UserConfiguration {
290+
291+
@Bean
292+
public ClientFactoryBean<MyClient> myClient() {
293+
return new ClientFactoryBean<>(...);
294+
}
295+
296+
}
297+
----
298+
======
299+
300+
If the `FactoryBean` bean definition is registered programmatically, make sure to follow these steps:
301+
302+
1. Use `RootBeanDefinition`.
303+
2. Set the `beanClass` to the `FactoryBean` class so that AOT knows that it is an intermediate layer.
304+
3. Set the `ResolvableType` to a resolved generic, which makes sure the most precise type is exposed.
305+
306+
The following example showcases a basic definition:
307+
308+
[tabs]
309+
======
310+
Java::
311+
+
312+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
313+
----
314+
RootBeanDefinition beanDefinition = new RootBeanDefinition(ClientFactoryBean.class);
315+
beanDefinition.setTargetType(ResolvableType.forClassWithGenerics(ClientFactoryBean.class, MyClient.class));
316+
// ...
317+
registry.registerBeanDefinition("myClient", beanDefinition);
318+
----
319+
======
320+
196321

197322
[[aot.hints]]
198323
== Runtime Hints

0 commit comments

Comments
 (0)