diff --git a/spring-beans/spring-beans.gradle b/spring-beans/spring-beans.gradle index b407bf0ed24..c4fb10eb320 100644 --- a/spring-beans/spring-beans.gradle +++ b/spring-beans/spring-beans.gradle @@ -16,4 +16,5 @@ dependencies { testImplementation(project(":spring-core-test")) testImplementation(testFixtures(project(":spring-core"))) testImplementation("jakarta.annotation:jakarta.annotation-api") + testImplementation("javax.inject:javax.inject") } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHints.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHints.java index c4e46f8ecd8..439b1fb30e4 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHints.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHints.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -24,16 +24,24 @@ import org.springframework.aot.hint.TypeReference; import org.springframework.lang.Nullable; /** - * {@link RuntimeHintsRegistrar} for Jakarta annotations. + * {@link RuntimeHintsRegistrar} for Jakarta annotations and their pre-Jakarta equivalents. * * @author Brian Clozel + * @author Sam Brannen */ class JakartaAnnotationsRuntimeHints implements RuntimeHintsRegistrar { @Override public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { - Stream.of("jakarta.inject.Inject", "jakarta.inject.Provider", "jakarta.inject.Qualifier").forEach(typeName -> - hints.reflection().registerType(TypeReference.of(typeName))); + // javax.inject.Provider is omitted from the list, since we do not currently load + // it via reflection. + Stream.of( + "jakarta.inject.Inject", + "jakarta.inject.Provider", + "jakarta.inject.Qualifier", + "javax.inject.Inject", + "javax.inject.Qualifier" + ).forEach(typeName -> hints.reflection().registerType(TypeReference.of(typeName))); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java index 8cf27dfc839..cf552f996e0 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java @@ -47,11 +47,13 @@ import org.springframework.util.ObjectUtils; * against {@link Qualifier qualifier annotations} on the field or parameter to be autowired. * Also supports suggested expression values through a {@link Value value} annotation. * - *
Also supports JSR-330's {@link jakarta.inject.Qualifier} annotation, if available. + *
Also supports JSR-330's {@link jakarta.inject.Qualifier} annotation (as well as its + * pre-Jakarta {@code javax.inject.Qualifier} equivalent), if available. * * @author Mark Fisher * @author Juergen Hoeller * @author Stephane Nicoll + * @author Sam Brannen * @since 2.5 * @see AutowireCandidateQualifier * @see Qualifier @@ -65,9 +67,10 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa /** - * Create a new QualifierAnnotationAutowireCandidateResolver - * for Spring's standard {@link Qualifier} annotation. - *
Also supports JSR-330's {@link jakarta.inject.Qualifier} annotation, if available. + * Create a new {@code QualifierAnnotationAutowireCandidateResolver} for Spring's + * standard {@link Qualifier} annotation. + *
Also supports JSR-330's {@link jakarta.inject.Qualifier} annotation (as well as
+ * its pre-Jakarta {@code javax.inject.Qualifier} equivalent), if available.
*/
@SuppressWarnings("unchecked")
public QualifierAnnotationAutowireCandidateResolver() {
@@ -76,6 +79,13 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
this.qualifierTypes.add((Class extends Annotation>) ClassUtils.forName("jakarta.inject.Qualifier",
QualifierAnnotationAutowireCandidateResolver.class.getClassLoader()));
}
+ catch (ClassNotFoundException ex) {
+ // JSR-330 API (as included in Jakarta EE) not available - simply skip.
+ }
+ try {
+ this.qualifierTypes.add((Class extends Annotation>) ClassUtils.forName("javax.inject.Qualifier",
+ QualifierAnnotationAutowireCandidateResolver.class.getClassLoader()));
+ }
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java
index 9dae6cf67cb..ef2e236fb68 100644
--- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java
+++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2023 the original author or authors.
+ * Copyright 2002-2024 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.
@@ -35,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link JakartaAnnotationsRuntimeHints}.
*
* @author Brian Clozel
+ * @author Sam Brannen
*/
class JakartaAnnotationsRuntimeHintsTests {
@@ -62,4 +63,14 @@ class JakartaAnnotationsRuntimeHintsTests {
assertThat(RuntimeHintsPredicates.reflection().onType(Qualifier.class)).accepts(this.hints);
}
+ @Test // gh-33345
+ void javaxInjectAnnotationHasHints() {
+ assertThat(RuntimeHintsPredicates.reflection().onType(javax.inject.Inject.class)).accepts(this.hints);
+ }
+
+ @Test // gh-33345
+ void javaxQualifierAnnotationHasHints() {
+ assertThat(RuntimeHintsPredicates.reflection().onType(javax.inject.Qualifier.class)).accepts(this.hints);
+ }
+
}
diff --git a/spring-context/src/test/java/org/springframework/beans/factory/support/InjectAnnotationAutowireContextTests.java b/spring-context/src/test/java/org/springframework/beans/factory/support/InjectAnnotationAutowireContextTests.java
index b4353ea40ae..48d19fbf7e8 100644
--- a/spring-context/src/test/java/org/springframework/beans/factory/support/InjectAnnotationAutowireContextTests.java
+++ b/spring-context/src/test/java/org/springframework/beans/factory/support/InjectAnnotationAutowireContextTests.java
@@ -32,6 +32,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.UnsatisfiedDependencyException;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.support.GenericApplicationContext;
@@ -39,9 +40,11 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
- * Integration tests for handling JSR-303 {@link jakarta.inject.Qualifier} annotations.
+ * Integration tests for handling JSR-330 {@link jakarta.inject.Qualifier} and
+ * {@link javax.inject.Qualifier} annotations.
*
* @author Juergen Hoeller
+ * @author Sam Brannen
* @since 3.0
*/
class InjectAnnotationAutowireContextTests {
@@ -304,6 +307,26 @@ class InjectAnnotationAutowireContextTests {
assertThat(bean.getPerson().getName()).isEqualTo(JUERGEN);
}
+ @Test // gh-33345
+ void autowiredConstructorArgumentResolvesJakartaNamedCandidate() {
+ Class