diff --git a/framework-docs/modules/ROOT/pages/core/beans/annotation-config/autowired.adoc b/framework-docs/modules/ROOT/pages/core/beans/annotation-config/autowired.adoc index 8f99eac7d2f..015e73b748f 100644 --- a/framework-docs/modules/ROOT/pages/core/beans/annotation-config/autowired.adoc +++ b/framework-docs/modules/ROOT/pages/core/beans/annotation-config/autowired.adoc @@ -444,9 +444,9 @@ through Java 8's `java.util.Optional`, as the following example shows: } ---- -You can also use a `@Nullable` annotation (of any kind in any package -- for example, -`javax.annotation.Nullable` from JSR-305) or just leverage Kotlin built-in null-safety -support: +You can also use a parameter-level `@Nullable` annotation (of any kind in any package -- +for example, `javax.annotation.Nullable` from JSR-305) or just leverage Kotlin built-in +null-safety support: [tabs] ====== @@ -477,6 +477,13 @@ Kotlin:: ---- ====== +[NOTE] +==== +A type-level `@Nullable` annotation such as from JSpecify is not supported in Spring +Framework 6.2 yet. You need to upgrade to Spring Framework 7.0 where the framework +detects type-level annotations and consistently declares JSpecify in its own codebase. +==== + You can also use `@Autowired` for interfaces that are well-known resolvable dependencies: `BeanFactory`, `ApplicationContext`, `Environment`, `ResourceLoader`, `ApplicationEventPublisher`, and `MessageSource`. These interfaces and their extended diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java b/spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java index 2768d066c3b..9d5f1fbc945 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java @@ -47,6 +47,21 @@ import org.springframework.core.OrderComparator; * Alternatively, you may implement the specific methods that your callers expect, * for example, just {@link #getObject()} or {@link #getIfAvailable()}. * + *

Note that {@link #getObject()} never returns {@code null} - it will throw a + * {@link NoSuchBeanDefinitionException} instead -, whereas {@link #getIfAvailable()} + * will return {@code null} if no matching bean is present at all. However, both + * methods will throw a {@link NoUniqueBeanDefinitionException} if more than one + * matching bean is found without a clear unique winner (see below). Last but not + * least, {@link #getIfUnique()} will return {@code null} both when no matching bean + * is found and when more than one matching bean is found without a unique winner. + * + *

Uniqueness is generally up to the container's candidate resolution algorithm + * but always honors the "primary" flag (with only one of the candidate beans marked + * as primary) and the "fallback" flag (with only one of the candidate beans not + * marked as fallback). The default-candidate flag is consistently taken into + * account as well, even for non-annotation-based injection points, with a single + * default candidate winning in case of no clear primary/fallback indication. + * * @author Juergen Hoeller * @since 4.3 * @param the object type @@ -187,7 +202,7 @@ public interface ObjectProvider extends ObjectFactory, Iterable { * if unique (not called otherwise) * @throws BeansException in case of creation errors * @since 5.0 - * @see #getIfAvailable() + * @see #getIfUnique() */ default void ifUnique(Consumer dependencyConsumer) throws BeansException { T dependency = getIfUnique(); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index c48eb0ca921..c95ed68c9c4 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -1683,6 +1683,24 @@ class DefaultListableBeanFactoryTests { assertThat(lbf.containsSingleton("bd1")).isFalse(); } + @Test + void getBeanByTypeWithPrimaryAndUniqueNonFallbackDefinition() { + RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class); + bd1.setLazyInit(true); + bd1.setFallback(true); + RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class); + bd2.setPrimary(true); + bd2.setFallback(true); + RootBeanDefinition bd3 = new RootBeanDefinition(TestBean.class); + lbf.registerBeanDefinition("bd1", bd1); + lbf.registerBeanDefinition("bd2", bd2); + lbf.registerBeanDefinition("bd3", bd3); + + TestBean bean = lbf.getBean(TestBean.class); + assertThat(bean.getBeanName()).isEqualTo("bd2"); + assertThat(lbf.containsSingleton("bd1")).isFalse(); + } + @Test void getBeanByTypeWithUniqueNonFallbackAndUniqueNonDefaultDefinition() { RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);