From f0de3a9924130a450949e7559646520196f53890 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 9 Nov 2021 23:01:31 +0100 Subject: [PATCH] Recommend ObjectProvider as alternative to @Lazy for optional dependencies Closes gh-27649 --- .../context/annotation/Lazy.java | 7 +- src/docs/asciidoc/core/core-beans.adoc | 148 ++++++++++-------- 2 files changed, 86 insertions(+), 69 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Lazy.java b/spring-context/src/main/java/org/springframework/context/annotation/Lazy.java index 9d04a9df26ee..8369957f5917 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/Lazy.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/Lazy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,6 +47,11 @@ * or {@link javax.inject.Inject}: In that context, it leads to the creation of a * lazy-resolution proxy for all affected dependencies, as an alternative to using * {@link org.springframework.beans.factory.ObjectFactory} or {@link javax.inject.Provider}. + * Please note that such a lazy-resolution proxy will always be injected; if the target + * dependency does not exist, you will only be able to find out through an exception on + * invocation. As a consequence, such an injection point results in unintuitive behavior + * for optional dependencies. For a programmatic equivalent, allowing for lazy references + * with more sophistication, consider {@link org.springframework.beans.factory.ObjectProvider}. * * @author Chris Beams * @author Juergen Hoeller diff --git a/src/docs/asciidoc/core/core-beans.adoc b/src/docs/asciidoc/core/core-beans.adoc index f02e860c0083..4dc5c4a93b78 100644 --- a/src/docs/asciidoc/core/core-beans.adoc +++ b/src/docs/asciidoc/core/core-beans.adoc @@ -1024,7 +1024,7 @@ by type without help. Consider the following class: class ExampleBean( private val years: Int, // Number of years to calculate the Ultimate Answer - private val ultimateAnswer: String// The Answer to Life, the Universe, and Everything + private val ultimateAnswer: String // The Answer to Life, the Universe, and Everything ) ---- @@ -1608,7 +1608,7 @@ listings shows how to use the `parent` attribute: ---- - + ---- @@ -1690,7 +1690,7 @@ respectively. The following example shows how to use them: - + @@ -1834,7 +1834,7 @@ class SomeClass { When the `accounts` property of the `something` bean is prepared for injection, the generics information about the element type of the strongly-typed `Map` is available by reflection. Thus, Spring's type conversion infrastructure recognizes the -various value elements as being of type `Float`, and the string values (`9.99, 2.75`, and +various value elements as being of type `Float`, and the string values (`9.99`, `2.75`, and `3.99`) are converted into an actual `Float` type. @@ -2028,7 +2028,7 @@ not commonly used since the plain order of declaration is usually sufficient the In practice, the constructor resolution <> is quite efficient in matching arguments, so unless you really need to, we recommend using the name notation -through-out your configuration. +throughout your configuration. [[beans-compound-property-names]] @@ -2503,13 +2503,13 @@ declared return type of the lookup method: public abstract class CommandManager { public Object process(Object commandState) { - MyCommand command = createCommand(); + Command command = createCommand(); command.setState(commandState); return command.execute(); } @Lookup - protected abstract MyCommand createCommand(); + protected abstract Command createCommand(); } ---- [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] @@ -2604,9 +2604,9 @@ interface provides the new method definition, as the following example shows: .Kotlin ---- /** - * meant to be used to override the existing computeValue(String) - * implementation in MyValueCalculator - */ + * meant to be used to override the existing computeValue(String) + * implementation in MyValueCalculator + */ class ReplacementComputeValue : MethodReplacer { override fun reimplement(obj: Any, method: Method, args: Array): Any { @@ -2806,7 +2806,7 @@ prototype-scoped bean repeatedly at runtime. You cannot dependency-inject a prototype-scoped bean into your singleton bean, because that injection occurs only once, when the Spring container instantiates the singleton bean and resolves and injects its dependencies. If you need a new instance of a prototype bean at -runtime more than once, see <> +runtime more than once, see <>. @@ -2984,7 +2984,7 @@ The Spring container creates a new instance of the `AppPreferences` bean by usin `appPreferences` bean is scoped at the `ServletContext` level and stored as a regular `ServletContext` attribute. This is somewhat similar to a Spring singleton bean but differs in two important ways: It is a singleton per `ServletContext`, not per Spring -'ApplicationContext' (for which there may be several in any given web application), +`ApplicationContext` (for which there may be several in any given web application), and it is actually exposed and therefore visible as a `ServletContext` attribute. When using annotation-driven components or Java configuration, you can use the @@ -3039,7 +3039,7 @@ constructor or setter argument or autowired field) as `ObjectFactory`, which delivers +As an extended variant, you may declare `ObjectProvider` which delivers several additional access variants, including `getIfAvailable` and `getIfUnique`. The JSR-330 variant of this is called `Provider` and is used with a `Provider` @@ -3100,7 +3100,7 @@ to the HTTP `Session`-scoped bean (`userPreferences`). The salient point here is `userManager` bean is a singleton: it is instantiated exactly once per container, and its dependencies (in this case only one, the `userPreferences` bean) are also injected only once. This means that the `userManager` bean operates only on the -exact same `userPreferences` object (that is, the one with which it was originally injected. +exact same `userPreferences` object (that is, the one with which it was originally injected). This is not the behavior you want when injecting a shorter-lived scoped bean into a longer-lived scoped bean (for example, injecting an HTTP `Session`-scoped collaborating @@ -3350,8 +3350,9 @@ of the scope. You can also do the `Scope` registration declaratively, by using t ---- -NOTE: When you place `` in a `FactoryBean` implementation, it is the factory -bean itself that is scoped, not the object returned from `getObject()`. +NOTE: When you place `` within a `` declaration for a +`FactoryBean` implementation, it is the factory bean itself that is scoped, not the object +returned from `getObject()`. @@ -3927,7 +3928,7 @@ shows the definition of the BeanNameAware interface: ---- The callback is invoked after population of normal bean properties but before an -initialization callback such as `InitializingBean`, `afterPropertiesSet`, or a custom +initialization callback such as `InitializingBean.afterPropertiesSet()` or a custom init-method. @@ -3959,7 +3960,7 @@ dependency type. The following table summarizes the most important `Aware` inter | `BeanFactoryAware` | Declaring `BeanFactory`. -| <> +| <> | `BeanNameAware` | Name of the declaring bean. @@ -4146,7 +4147,7 @@ or it may wrap a bean with a proxy. Some Spring AOP infrastructure classes are implemented as bean post-processors in order to provide proxy-wrapping logic. An `ApplicationContext` automatically detects any beans that are defined in the -configuration metadata that implements the `BeanPostProcessor` interface. The +configuration metadata that implement the `BeanPostProcessor` interface. The `ApplicationContext` registers these beans as post-processors so that they can be called later, upon bean creation. Bean post-processors can be deployed in the container in the same fashion as any other beans. @@ -4544,22 +4545,22 @@ Java as opposed to a (potentially) verbose amount of XML, you can create your ow `FactoryBean`, write the complex initialization inside that class, and then plug your custom `FactoryBean` into the container. -The `FactoryBean` interface provides three methods: +The `FactoryBean` interface provides three methods: -* `Object getObject()`: Returns an instance of the object this factory creates. The +* `T getObject()`: Returns an instance of the object this factory creates. The instance can possibly be shared, depending on whether this factory returns singletons or prototypes. * `boolean isSingleton()`: Returns `true` if this `FactoryBean` returns singletons or - `false` otherwise. -* `Class getObjectType()`: Returns the object type returned by the `getObject()` method + `false` otherwise. The default implementation of this method returns `true`. +* `Class getObjectType()`: Returns the object type returned by the `getObject()` method or `null` if the type is not known in advance. -The `FactoryBean` concept and interface is used in a number of places within the Spring +The `FactoryBean` concept and interface are used in a number of places within the Spring Framework. More than 50 implementations of the `FactoryBean` interface ship with Spring itself. When you need to ask a container for an actual `FactoryBean` instance itself instead of -the bean it produces, preface the bean's `id` with the ampersand symbol (`&`) when +the bean it produces, prefix the bean's `id` with the ampersand symbol (`&`) when calling the `getBean()` method of the `ApplicationContext`. So, for a given `FactoryBean` with an `id` of `myBean`, invoking `getBean("myBean")` on the container returns the product of the `FactoryBean`, whereas invoking `getBean("&myBean")` returns the @@ -4677,11 +4678,11 @@ example: ---- class SimpleMovieLister { - @Required - lateinit var movieFinder: MovieFinder + @Required + lateinit var movieFinder: MovieFinder - // ... -} + // ... + } ---- This annotation indicates that the affected bean property must be populated at @@ -5377,7 +5378,7 @@ Letting qualifier values select against target bean names, within the type-match candidates, does not require a `@Qualifier` annotation at the injection point. If there is no other resolution indicator (such as a qualifier or a primary marker), for a non-unique dependency situation, Spring matches the injection point name -(that is, the field name or parameter name) against the target bean names and choose the +(that is, the field name or parameter name) against the target bean names and chooses the same-named candidate, if any. ==== @@ -5392,7 +5393,7 @@ matching an `account` qualifier against beans marked with the same qualifier lab For beans that are themselves defined as a collection, `Map`, or array type, `@Resource` is a fine solution, referring to the specific collection or array bean by unique name. -That said, as of 4.3, collection, you can match `Map`, and array types through Spring's +That said, as of 4.3, you can match collection, `Map`, and array types through Spring's `@Autowired` type matching algorithm as well, as long as the element type information is preserved in `@Bean` return type signatures or collection inheritance hierarchies. In this case, you can use qualifier values to select among same-typed collections, @@ -6046,14 +6047,14 @@ example shows: [source,java,indent=0,subs="verbatim,quotes",role="primary"] .Java ---- - @Configuration - public class AppConfig { + @Configuration + public class AppConfig { - @Bean - public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { - return new PropertySourcesPlaceholderConfigurer(); - } - } + @Bean + public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { + return new PropertySourcesPlaceholderConfigurer(); + } + } ---- [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] .Kotlin @@ -6079,7 +6080,7 @@ will get properties from `application.properties` and `application.yml` files. Built-in converter support provided by Spring allows simple type conversion (to `Integer` or `int` for example) to be automatically handled. Multiple comma-separated values can be -automatically converted to String array without extra effort. +automatically converted to `String` array without extra effort. It is possible to provide a default value as following: @@ -6103,8 +6104,8 @@ It is possible to provide a default value as following: class MovieRecommender(@Value("\${catalog.name:defaultCatalog}") private val catalog: String) ---- -A Spring `BeanPostProcessor` uses a `ConversionService` behind the scene to handle the -process for converting the String value in `@Value` to the target type. If you want to +A Spring `BeanPostProcessor` uses a `ConversionService` behind the scenes to handle the +process for converting the `String` value in `@Value` to the target type. If you want to provide conversion support for your own custom type, you can provide your own `ConversionService` bean instance as the following example shows: @@ -6130,7 +6131,7 @@ provide conversion support for your own custom type, you can provide your own @Bean fun conversionService(): ConversionService { - return DefaultFormattingConversionService().apply { + return DefaultFormattingConversionService().apply { addConverter(MyCustomConverter()) } } @@ -6319,7 +6320,7 @@ is meta-annotated with `@Component`, as the following example shows: // ... } ---- -<1> The `Component` causes `@Service` to be treated in the same way as `@Component`. +<1> The `@Component` causes `@Service` to be treated in the same way as `@Component`. [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] .Kotlin @@ -6333,7 +6334,7 @@ is meta-annotated with `@Component`, as the following example shows: // ... } ---- -<1> The `Component` causes `@Service` to be treated in the same way as `@Component`. +<1> The `@Component` causes `@Service` to be treated in the same way as `@Component`. You can also combine meta-annotations to create "`composed annotations`". For example, the `@RestController` annotation from Spring MVC is composed of `@Controller` and @@ -6595,7 +6596,7 @@ and using "`stub`" repositories instead: includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"), excludeFilters = @Filter(Repository.class)) public class AppConfig { - ... + // ... } ---- [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] @@ -6679,9 +6680,11 @@ 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` annotation -on injection points marked with `@Autowired` or `@Inject`. In this context, it -leads to the injection of a lazy-resolution proxy. +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: @@ -7161,7 +7164,7 @@ configuration, as shown in the following example: With Gradle 4.6 and later, the dependency should be declared in the `annotationProcessor` configuration, as shown in the following example: -[source,groovy,indent=0subs="verbatim,quotes,attributes"] +[source,groovy,indent=0,subs="verbatim,quotes,attributes"] ---- dependencies { annotationProcessor "org.springframework:spring-context-indexer:{spring-version}" @@ -7179,8 +7182,8 @@ TIP: The index is enabled automatically when a `META-INF/spring.components` file on the classpath. If an index is partially available for some libraries (or use cases) but could not be built for the whole application, you can fall back to a regular classpath arrangement (as though no index were present at all) by setting `spring.index.ignore` to -`true`, either as a JVM system property or in a `spring.properties` file at the root of -the classpath. +`true`, either as a JVM system property or via the +<> mechanism. @@ -7635,7 +7638,7 @@ to reduce subtle bugs that can be hard to track down when operating in "`lite`" **** The `@Bean` and `@Configuration` annotations are discussed in depth in the following sections. -First, however, we cover the various ways of creating a spring container using by +First, however, we cover the various ways of creating a spring container by using Java-based configuration. @@ -7762,7 +7765,7 @@ To enable component scanning, you can annotate your `@Configuration` class as fo @Configuration @ComponentScan(basePackages = "com.acme") // <1> public class AppConfig { - ... + // ... } ---- <1> This annotation enables component scanning. @@ -7896,6 +7899,7 @@ init-param): `@Bean` is a method-level annotation and a direct analog of the XML `` element. The annotation supports some of the attributes offered by ``, such as: + * <> * <> * <> @@ -7984,7 +7988,7 @@ return type, as the following example shows: However, this limits the visibility for advance type prediction to the specified interface type (`TransferService`). Then, with the full type (`TransferServiceImpl`) -known to the container only once, the affected singleton bean has been instantiated. +known to the container only once the affected singleton bean has been instantiated. Non-lazy singleton beans get instantiated according to their declaration order, so you may see different type matching results depending on when another component tries to match by a non-declared type (such as `@Autowired TransferServiceImpl`, @@ -8242,8 +8246,10 @@ Spring offers a convenient way of working with scoped dependencies through <>. The easiest way to create such a proxy when using the XML configuration is the `` element. Configuring your beans in Java with a `@Scope` annotation offers equivalent support -with the `proxyMode` attribute. The default is no proxy (`ScopedProxyMode.NO`), -but you can specify `ScopedProxyMode.TARGET_CLASS` or `ScopedProxyMode.INTERFACES`. +with the `proxyMode` attribute. The default is `ScopedProxyMode.DEFAULT`, which +typically indicates that no scoped proxy should be created unless a different default +has been configured at the component-scan instruction level. You can specify +`ScopedProxyMode.TARGET_CLASS`, `ScopedProxyMode.INTERFACES` or `ScopedProxyMode.NO`. If you port the scoped proxy example from the XML reference documentation (see <>) to our `@Bean` using Java, @@ -8297,7 +8303,7 @@ as the following example shows: @Configuration public class AppConfig { - @Bean(name = "myThing") + @Bean("myThing") public Thing thing() { return new Thing(); } @@ -8390,7 +8396,7 @@ annotation, as the following example shows: === Using the `@Configuration` annotation `@Configuration` is a class-level annotation indicating that an object is a source of -bean definitions. `@Configuration` classes declare beans through public `@Bean` annotated +bean definitions. `@Configuration` classes declare beans through `@Bean`-annotated methods. Calls to `@Bean` methods on `@Configuration` classes can also be used to define inter-bean dependencies. See <> for a general introduction. @@ -9139,7 +9145,7 @@ method that returns `true` or `false`. For example, the following listing shows val attrs = metadata.getAllAnnotationAttributes(Profile::class.java.name) if (attrs != null) { for (value in attrs["value"]!!) { - if (context.environment.acceptsProfiles(Profiles .of(*value as Array))) { + if (context.environment.acceptsProfiles(Profiles.of(*value as Array))) { return true } } @@ -9903,7 +9909,7 @@ as a way to provide a default definition for one or more beans. If any profile is enabled, the default profile does not apply. You can change the name of the default profile by using `setDefaultProfiles()` on -the `Environment` or ,declaratively, by using the `spring.profiles.default` property. +the `Environment` or, declaratively, by using the `spring.profiles.default` property. @@ -10222,8 +10228,8 @@ bean with the same name. If it does, it uses that bean as the `MessageSource`. I `DelegatingMessageSource` is instantiated in order to be able to accept calls to the methods defined above. -Spring provides two `MessageSource` implementations, `ResourceBundleMessageSource` and -`StaticMessageSource`. Both implement `HierarchicalMessageSource` in order to do nested +Spring provides three `MessageSource` implementations, `ResourceBundleMessageSource`, `ReloadableResourceBundleMessageSource` +and `StaticMessageSource`. All of them implement `HierarchicalMessageSource` in order to do nested messaging. The `StaticMessageSource` is rarely used but provides programmatic ways to add messages to the source. The following example shows `ResourceBundleMessageSource`: @@ -10408,6 +10414,10 @@ You can also use the `MessageSourceAware` interface to acquire a reference to an `ApplicationContext` that implements the `MessageSourceAware` interface is injected with the application context's `MessageSource` when the bean is created and configured. +NOTE: Because Spring's `MessageSource` is based on Java's `ResourceBundle`, it does not merge +bundles with the same base name, but will only use the first bundle found. +Subsequent message bundles with the same base name are ignored. + NOTE: As an alternative to `ResourceBundleMessageSource`, Spring provides a `ReloadableResourceBundleMessageSource` class. This variant supports the same bundle file format but is more flexible than the standard JDK based @@ -10718,7 +10728,7 @@ following example shows how to do so: ---- It is also possible to add additional runtime filtering by using the `condition` attribute -of the annotation that defines a <> , which should match +of the annotation that defines a <>, which should match to actually invoke the method for a particular event. The following example shows how our notifier can be rewritten to be invoked only if the @@ -10830,10 +10840,12 @@ The following example shows how to do so: Be aware of the following limitations when using asynchronous events: * If an asynchronous event listener throws an `Exception`, it is not propagated to the - caller. See `AsyncUncaughtExceptionHandler` for more details. + caller. See + {api-spring-framework}/aop/interceptor/AsyncUncaughtExceptionHandler.html[`AsyncUncaughtExceptionHandler`] + for more details. * Asynchronous event listener methods cannot publish a subsequent event by returning a value. If you need to publish another event as the result of the processing, inject an - {api-spring-framework}/aop/interceptor/AsyncUncaughtExceptionHandler.html[`ApplicationEventPublisher`] + {api-spring-framework}/context/ApplicationEventPublisher.html[`ApplicationEventPublisher`] to publish the event manually. @@ -11021,8 +11033,8 @@ For a simple deployment of a Spring ApplicationContext as a Java EE RAR file: . Package all application classes into a RAR file (which is a standard JAR file with a different file extension). -.Add all required library JARs into the root of the RAR archive. -.Add a +. Add all required library JARs into the root of the RAR archive. +. Add a `META-INF/ra.xml` deployment descriptor (as shown in the {api-spring-framework}/jca/context/SpringContextResourceAdapter.html[javadoc for `SpringContextResourceAdapter`]) and the corresponding Spring XML bean definition file(s) (typically `META-INF/applicationContext.xml`). @@ -11116,7 +11128,7 @@ The following table lists features provided by the `BeanFactory` and | No | Yes -| Convenient `MessageSource` access (for internalization) +| Convenient `MessageSource` access (for internationalization) | No | Yes