While your application may interact with an interface that a bean implements, it is still very important to declare the most precise type.
The AOT engine performs additional checks on the bean type, such as detecting the presence of `@Autowired` members or lifecycle callback methods.
For `@Configuration` classes, make sure that the return type of a `@Bean` factory method is as precise as possible.
For `@Configuration` classes, make sure that the return type of an `@Bean` factory method is as precise as possible.
Consider the following example:
[tabs]
@ -602,9 +602,12 @@ A number of convenient annotations are also provided for common use cases.
@@ -602,9 +602,12 @@ A number of convenient annotations are also provided for common use cases.
[[aot.hints.import-runtime-hints]]
=== `@ImportRuntimeHints`
`RuntimeHintsRegistrar` implementations allow you to get a callback to the `RuntimeHints` instance managed by the AOT engine.
Implementations of this interface can be registered using `@ImportRuntimeHints` on any Spring bean or `@Bean` factory method.
`RuntimeHintsRegistrar` implementations are detected and invoked at build time.
on any Spring bean or `@Bean` factory method. `RuntimeHintsRegistrar` implementations are
detected and invoked at build time.
include-code::./SpellCheckService[]
@ -620,8 +623,10 @@ It is also possible to register an implementation statically by adding an entry
@@ -620,8 +623,10 @@ It is also possible to register an implementation statically by adding an entry
{spring-framework-api}/aot/hint/annotation/Reflective.html[`@Reflective`] provides an idiomatic way to flag the need for reflection on an annotated element.
For instance, `@EventListener` is meta-annotated with `@Reflective` since the underlying implementation invokes the annotated method using reflection.
Out-of-the-box, only Spring beans are considered, but you can opt-in for scanning using `@ReflectiveScan`.
In the example below, all types in the `com.example.app` package and its subpackages are considered:
Out-of-the-box, only Spring beans are considered, but you can opt-in for scanning using
{spring-framework-api}/context/annotation/ReflectiveScan.html[`@ReflectiveScan`]. In the
example below, all types in the `com.example.app` package and its subpackages are
considered:
include-code::./MyConfiguration[]
@ -638,9 +643,9 @@ An example of such customization is covered in the next section.
@@ -638,9 +643,9 @@ An example of such customization is covered in the next section.
[[aot.hints.register-reflection]]
=== `@RegisterReflection`
{spring-framework-api}/aot/hint/annotation/RegisterReflection.html[`@RegisterReflection`] is a specialization of `@Reflective` that provides a declarative way of registering reflection for arbitrary types.
{spring-framework-api}/aot/hint/annotation/RegisterReflection.html[`@RegisterReflection`] is a specialization of `@Reflective` that provides a declarative way to register reflection for arbitrary types.
NOTE: As a specialization of `@Reflective`, this is also detected if you are using `@ReflectiveScan`.
NOTE: As a specialization of `@Reflective`, `@RegisterReflection` is also detected if you are using `@ReflectiveScan`.
In the following example, public constructors and public methods can be invoked via reflection on `AccountService`:
`@RegisterReflection` can be applied to any target type at the class level, but it can also be applied directly to a method to better indicate where the hints are actually required.
`@RegisterReflection` can be used as a meta-annotation to provide more specific needs.
{spring-framework-api}/aot/hint/annotation/RegisterReflectionForBinding.html[`@RegisterReflectionForBinding`] is such composed annotation and registers the need for serializing arbitrary types.
`@RegisterReflection` can be used as a meta-annotation to support more specific needs.
{spring-framework-api}/aot/hint/annotation/RegisterReflectionForBinding.html[`@RegisterReflectionForBinding`] is a composed annotation that is meta-annotated with `@RegisterReflection` and registers the need for serializing arbitrary types.
A typical use case is the use of DTOs that the container cannot infer, such as using a web client within a method body.
The following example registers `Order` for serialization.
@ -660,11 +665,66 @@ This registers hints for constructors, fields, properties, and record components
@@ -660,11 +665,66 @@ This registers hints for constructors, fields, properties, and record components
Hints are also registered for types transitively used on properties and record components.
In other words, if `Order` exposes others types, hints are registered for those as well.
[[aot.hints.convention-based-conversion]]
=== Runtime Hints for Convention-based Conversion
Although the core container provides built-in support for automatic conversion of many
common types (see xref:core/validation/convert.adoc[Spring Type Conversion]), some
conversions are supported via a convention-based algorithm that relies on reflection.
Specifically, if there is no explicit `Converter` registered with the `ConversionService`
for a particular source → target type pair, the internal `ObjectToObjectConverter`
will attempt to use conventions to convert a source object to a target type by delegating
to a method on the source object or to a static factory method or constructor on the
target type. Since this convention-based algorithm can be applied to arbitrary types at
runtime, the core container is not able to infer the runtime hints necessary to support
such reflection.
If you encounter convention-based conversion issues within a native image resulting from
lacking runtime hints, you can register the necessary hints programmatically. For
example, if your application requires a conversion from `java.time.Instant` to
`java.sql.Timestamp` and relies on `ObjectToObjectConverter` to invoke
`java.sql.Timestamp.from(Instant)` using reflection, you could implement a custom
`RuntimeHintsRegitrar` to support this use case within a native image, as demonstrated in
the following example.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
public class TimestampConversionRuntimeHints implements RuntimeHintsRegistrar {
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
@ -72,9 +72,9 @@ When you need to centralize the conversion logic for an entire class hierarchy
@@ -72,9 +72,9 @@ When you need to centralize the conversion logic for an entire class hierarchy
}
----
Parameterize S to be the type you are converting from and R to be the base type defining
Parameterize `S` to be the type you are converting from and `R` to be the base type defining
the __range__ of classes you can convert to. Then implement `getConverter(Class<T>)`,
where T is a subclass of R.
where `T` is a subclass of `R`.
Consider the `StringToEnumConverterFactory` as an example:
@ -107,13 +107,15 @@ Consider the `StringToEnumConverterFactory` as an example:
@@ -107,13 +107,15 @@ Consider the `StringToEnumConverterFactory` as an example:
[[core-convert-GenericConverter-SPI]]
== Using `GenericConverter`
When you require a sophisticated `Converter` implementation, consider using the
`GenericConverter` interface. With a more flexible but less strongly typed signature
than `Converter`, a `GenericConverter` supports converting between multiple source and
target types. In addition, a `GenericConverter` makes available source and target field
context that you can use when you implement your conversion logic. Such context lets a
type conversion be driven by a field annotation or by generic information declared on a
field signature. The following listing shows the interface definition of `GenericConverter`:
When you require a more sophisticated `Converter` implementation, consider using the
`GenericConverter` interface. With a more flexible but less strongly typed signature than
`Converter`, a `GenericConverter` supports converting between multiple source and target
types. In addition, a `GenericConverter` is provided source and target type descriptors
that you can use when you implement your conversion logic. Such type descriptors enable
type conversion to be driven by an annotation on the source of the descriptor (such as a
field or method) or by generic information declared in a field signature, method
signature, etc. The following listing shows the definition of the `GenericConverter`
@ -128,16 +130,17 @@ field signature. The following listing shows the interface definition of `Generi
@@ -128,16 +130,17 @@ field signature. The following listing shows the interface definition of `Generi
----
To implement a `GenericConverter`, have `getConvertibleTypes()` return the supported
source->target type pairs. Then implement `convert(Object, TypeDescriptor,
source → target type pairs. Then implement `convert(Object, TypeDescriptor,
TypeDescriptor)` to contain your conversion logic. The source `TypeDescriptor` provides
access to the source field that holds the value being converted. The target `TypeDescriptor`
provides access to the target field where the converted value is to be set.
access to the source field or method that holds the value being converted. The target
`TypeDescriptor` provides access to the target field or method where the converted value
is to be set.
A good example of a `GenericConverter` is a converter that converts between a Java array
and a collection. Such an `ArrayToCollectionConverter` introspects the field that declares
the target collection type to resolve the collection's element type. This lets each
element in the source array be converted to the collection element type before the
collection is set on the target field.
and a collection. Such an `ArrayToCollectionConverter` introspects the field or method
that declares the target collection type to resolve the collection's element type. This
lets each element in the source array be converted to the collection element type before
the collection is set on the target field or supplied to the target method or constructor.
NOTE: Because `GenericConverter` is a more complex SPI interface, you should use
it only when you need it. Favor `Converter` or `ConverterFactory` for basic type
Sometimes, you want a `Converter` to run only if a specific condition holds true. For
example, you might want to run a `Converter` only if a specific annotation is present
on the target field, or you might want to run a `Converter` only if a specific method
(such as a `static valueOf` method) is defined on the target class.
example, you might want to run a `Converter` only if a specific annotation is present on
the target field or method, or you might want to run a `Converter` only if a specific
method (such as a `static valueOf` method) is defined on the target type.
`ConditionalGenericConverter` is the union of the `GenericConverter` and
`ConditionalConverter` interfaces that lets you define such custom matching criteria:
@ -212,7 +215,7 @@ creating common `ConversionService` configurations.
@@ -212,7 +215,7 @@ creating common `ConversionService` configurations.
A `ConversionService` is a stateless object designed to be instantiated at application
startup and then shared between multiple threads. In a Spring application, you typically
configure a `ConversionService` instance for each Spring container (or `ApplicationContext`).
Spring picks up that `ConversionService` and uses it whenever a type
Spring picks up that `ConversionService` and uses it whenever type
conversion needs to be performed by the framework. You can also inject this
`ConversionService` into any of your beans and invoke it directly.
@ -249,7 +252,8 @@ It is also common to use a `ConversionService` within a Spring MVC application.
@@ -249,7 +252,8 @@ It is also common to use a `ConversionService` within a Spring MVC application.
xref:web/webmvc/mvc-config/conversion.adoc[Conversion and Formatting] in the Spring MVC chapter.
In certain situations, you may wish to apply formatting during conversion. See
xref:core/validation/format.adoc#format-FormatterRegistry-SPI[The `FormatterRegistry` SPI] for details on using `FormattingConversionServiceFactoryBean`.