From 39cfe136dad76dc89ca0f52229d0aa2911b76a55 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:49:22 +0100 Subject: [PATCH 1/4] Polishing --- .../interceptor/CacheOperationExpressionEvaluatorTests.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationExpressionEvaluatorTests.java b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationExpressionEvaluatorTests.java index 11625dce481..3b826775a54 100644 --- a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationExpressionEvaluatorTests.java +++ b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationExpressionEvaluatorTests.java @@ -86,7 +86,7 @@ class CacheOperationExpressionEvaluatorTests { AnnotatedClass target = new AnnotatedClass(); Method method = ReflectionUtils.findMethod( AnnotatedClass.class, "multipleCaching", Object.class, Object.class); - Object[] args = new Object[] {new Object(), new Object()}; + Object[] args = {"arg1", "arg2"}; Collection caches = Collections.singleton(new ConcurrentMapCache("test")); EvaluationContext evalCtx = this.eval.createEvaluationContext(caches, method, args, @@ -155,7 +155,7 @@ class CacheOperationExpressionEvaluatorTests { AnnotatedClass target = new AnnotatedClass(); Method method = ReflectionUtils.findMethod( AnnotatedClass.class, "multipleCaching", Object.class, Object.class); - Object[] args = new Object[] {new Object(), new Object()}; + Object[] args = new Object[] {"arg1", "arg2"}; Collection caches = Collections.singleton(new ConcurrentMapCache("test")); return this.eval.createEvaluationContext( caches, method, args, target, target.getClass(), method, result); From c457131f1c183e58c0b1193235eea02464267bad Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Thu, 24 Oct 2024 16:43:17 +0200 Subject: [PATCH 2/4] Fix heading level for "Programmatic bean registration" in AOT chapter (cherry picked from commit 9f0dbc405180ded52250d8b52c430a74c4cb00b8) --- framework-docs/modules/ROOT/pages/core/aot.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework-docs/modules/ROOT/pages/core/aot.adoc b/framework-docs/modules/ROOT/pages/core/aot.adoc index 9446fdc24c0..7914611b002 100644 --- a/framework-docs/modules/ROOT/pages/core/aot.adoc +++ b/framework-docs/modules/ROOT/pages/core/aot.adoc @@ -244,7 +244,7 @@ However, keep in mind that some optimizations are made at build time based on a This section lists the best practices that make sure your application is ready for AOT. [[aot.bestpractices.bean-registration]] -== Programmatic bean registration +=== Programmatic bean registration The AOT engine takes care of the `@Configuration` model and any callback that might be invoked as part of processing your configuration. If you need to register additional From fc7b8ae966a1a4ecb8a2e812b23b6ef5adff1b14 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Wed, 6 Nov 2024 17:46:06 +0100 Subject: [PATCH 3/4] Fix anchor name, consistently use title case, and polish wording (cherry picked from commit 2e6c8daec639b8194bf191b9f67056cde2a18f48) --- framework-docs/modules/ROOT/pages/core/aot.adoc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/framework-docs/modules/ROOT/pages/core/aot.adoc b/framework-docs/modules/ROOT/pages/core/aot.adoc index 7914611b002..cdd7cf71b4f 100644 --- a/framework-docs/modules/ROOT/pages/core/aot.adoc +++ b/framework-docs/modules/ROOT/pages/core/aot.adoc @@ -36,7 +36,7 @@ NOTE: At the moment, AOT is focused on allowing Spring applications to be deploy We intend to support more JVM-based use cases in future generations. [[aot.basics]] -== AOT engine overview +== AOT Engine Overview The entry point of the AOT engine for processing an `ApplicationContext` is `ApplicationContextAotGenerator`. It takes care of the following steps, based on a `GenericApplicationContext` that represents the application to optimize and a {spring-framework-api}/aot/generate/GenerationContext.html[`GenerationContext`]: @@ -225,7 +225,7 @@ When a `datasource` instance is required, a `BeanInstanceSupplier` is called. This supplier invokes the `dataSource()` method on the `dataSourceConfiguration` bean. [[aot.running]] -== Running with AOT optimizations +== Running with AOT Optimizations AOT is a mandatory step to transform a Spring application to a native executable, so it is automatically enabled when running in this mode. It is possible to use those optimizations @@ -244,7 +244,7 @@ However, keep in mind that some optimizations are made at build time based on a This section lists the best practices that make sure your application is ready for AOT. [[aot.bestpractices.bean-registration]] -=== Programmatic bean registration +=== Programmatic Bean Registration The AOT engine takes care of the `@Configuration` model and any callback that might be invoked as part of processing your configuration. If you need to register additional @@ -266,7 +266,7 @@ notion of a classpath. For cases like this, it is crucial that the scanning happ build time. [[aot.bestpractices.bean-type]] -=== Expose The Most Precise Bean Type +=== Expose the Most Precise Bean Type 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. @@ -326,21 +326,21 @@ However, this is not a best practice and flagging the preferred constructor with In case you are working on a code base that you cannot modify, you can set the {spring-framework-api}/beans/factory/support/AbstractBeanDefinition.html#PREFERRED_CONSTRUCTORS_ATTRIBUTE[`preferredConstructors` attribute] on the related bean definition to indicate which constructor should be used. -[[aot.bestpractices.comlext-data-structure]] -=== Avoid Complex Data Structure for Constructor Parameters and Properties +[[aot.bestpractices.complex-data-structures]] +=== Avoid Complex Data Structures for Constructor Parameters and Properties When crafting a `RootBeanDefinition` programmatically, you are not constrained in terms of types that you can use. For instance, you may have a custom `record` with several properties that your bean takes as a constructor argument. While this works fine with the regular runtime, AOT does not know how to generate the code of your custom data structure. A good rule of thumb is to keep in mind that bean definitions are an abstraction on top of several models. -Rather than using such structure, decomposing to simple types or referring to a bean that is built as such is recommended. +Rather than using such structures, decomposing to simple types or referring to a bean that is built as such is recommended. As a last resort, you can implement your own `org.springframework.aot.generate.ValueCodeGenerator$Delegate`. To use it, register its fully qualified name in `META-INF/spring/aot.factories` using the `Delegate` as the key. [[aot.bestpractices.custom-arguments]] -=== Avoid Creating Bean with Custom Arguments +=== Avoid Creating Beans with Custom Arguments Spring AOT detects what needs to be done to create a bean and translates that in generated code using an instance supplier. The container also supports creating a bean with {spring-framework-api}++/beans/factory/BeanFactory.html#getBean(java.lang.String,java.lang.Object...)++[custom arguments] that leads to several issues with AOT: From 0d9033592b4baa1e5790eb2e0d2a8c007101f4b8 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Thu, 7 Nov 2024 12:23:39 +0100 Subject: [PATCH 4/4] Document that circular dependencies should be avoided in AOT mode Closes gh-33786 --- framework-docs/modules/ROOT/pages/core/aot.adoc | 14 ++++++++++++++ .../ROOT/pages/core/beans/classpath-scanning.adoc | 6 +++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/framework-docs/modules/ROOT/pages/core/aot.adoc b/framework-docs/modules/ROOT/pages/core/aot.adoc index cdd7cf71b4f..2a53060d05f 100644 --- a/framework-docs/modules/ROOT/pages/core/aot.adoc +++ b/framework-docs/modules/ROOT/pages/core/aot.adoc @@ -352,6 +352,20 @@ For instance, autowiring on fields and methods will be skipped as they are handl Rather than having prototype-scoped beans created with custom arguments, we recommend a manual factory pattern where a bean is responsible for the creation of the instance. +[[aot.bestpractices.circular-dependencies]] +=== Avoid Circular Dependencies + +Certain use cases can result in circular dependencies between one or more beans. With the +regular runtime, it may be possible to wire those circular dependencies via `@Autowired` +on setter methods or fields. However, an AOT-optimized context will fail to start with +explicit circular dependencies. + +In an AOT-optimized application, you should therefore strive to avoid circular +dependencies. If that is not possible, you can use `@Lazy` injection points or +`ObjectProvider` to lazily access or retrieve the necessary collaborating beans. See +xref:core/beans/classpath-scanning.adoc#beans-factorybeans-annotations-lazy-injection-points[this tip] +for further information. + [[aot.bestpractices.factory-bean]] === FactoryBean diff --git a/framework-docs/modules/ROOT/pages/core/beans/classpath-scanning.adoc b/framework-docs/modules/ROOT/pages/core/beans/classpath-scanning.adoc index 8c067fed4f7..59d53dda157 100644 --- a/framework-docs/modules/ROOT/pages/core/beans/classpath-scanning.adoc +++ b/framework-docs/modules/ROOT/pages/core/beans/classpath-scanning.adoc @@ -480,11 +480,15 @@ factory method and other bean definition properties, such as a qualifier value t the `@Qualifier` annotation. Other method-level annotations that can be specified are `@Scope`, `@Lazy`, and custom qualifier annotations. -TIP: In addition to its role for component initialization, you can also place the `@Lazy` +[[beans-factorybeans-annotations-lazy-injection-points]] +[TIP] +==== +In addition to its role for component initialization, you can also place the `@Lazy` annotation on injection points marked with `@Autowired` or `@Inject`. In this context, it leads to the injection of a lazy-resolution proxy. However, such a proxy approach is rather limited. For sophisticated lazy interactions, in particular in combination with optional dependencies, we recommend `ObjectProvider` instead. +==== Autowired fields and methods are supported, as previously discussed, with additional support for autowiring of `@Bean` methods. The following example shows how to do so: