Reinstate qualifier support for JSR-330 @javax.inject.Named
This commit revises QualifierAnnotationAutowireCandidateResolver to reinstate "qualifier" support for the legacy JSR-330 @javax.inject.Named annotation. See gh-31090 Closes gh-33345
This commit is contained in:
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* <p>Also supports JSR-330's {@link jakarta.inject.Qualifier} annotation, if available.
|
||||
* <p>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.
|
||||
* <p>Also supports JSR-330's {@link jakarta.inject.Qualifier} annotation, if available.
|
||||
* Create a new {@code QualifierAnnotationAutowireCandidateResolver} for Spring's
|
||||
* standard {@link Qualifier} annotation.
|
||||
* <p>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.
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<JakartaNamedConstructorArgumentTestBean> testBeanClass = JakartaNamedConstructorArgumentTestBean.class;
|
||||
AnnotationConfigApplicationContext context =
|
||||
new AnnotationConfigApplicationContext(testBeanClass, JakartaCat.class, JakartaDog.class);
|
||||
JakartaNamedConstructorArgumentTestBean bean = context.getBean(testBeanClass);
|
||||
assertThat(bean.getAnimal1().getName()).isEqualTo("Jakarta Tiger");
|
||||
assertThat(bean.getAnimal2().getName()).isEqualTo("Jakarta Fido");
|
||||
}
|
||||
|
||||
@Test // gh-33345
|
||||
void autowiredConstructorArgumentResolvesJavaxNamedCandidate() {
|
||||
Class<JavaxNamedConstructorArgumentTestBean> testBeanClass = JavaxNamedConstructorArgumentTestBean.class;
|
||||
AnnotationConfigApplicationContext context =
|
||||
new AnnotationConfigApplicationContext(testBeanClass, JavaxCat.class, JavaxDog.class);
|
||||
JavaxNamedConstructorArgumentTestBean bean = context.getBean(testBeanClass);
|
||||
assertThat(bean.getAnimal1().getName()).isEqualTo("Javax Tiger");
|
||||
assertThat(bean.getAnimal2().getName()).isEqualTo("Javax Fido");
|
||||
}
|
||||
|
||||
@Test
|
||||
void autowiredFieldResolvesQualifiedCandidateWithDefaultValueAndNoValueOnBeanDefinition() {
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
@@ -541,6 +564,52 @@ class InjectAnnotationAutowireContextTests {
|
||||
}
|
||||
|
||||
|
||||
static class JakartaNamedConstructorArgumentTestBean {
|
||||
|
||||
private final Animal animal1;
|
||||
private final Animal animal2;
|
||||
|
||||
@jakarta.inject.Inject
|
||||
public JakartaNamedConstructorArgumentTestBean(@jakarta.inject.Named("Cat") Animal animal1,
|
||||
@jakarta.inject.Named("Dog") Animal animal2) {
|
||||
|
||||
this.animal1 = animal1;
|
||||
this.animal2 = animal2;
|
||||
}
|
||||
|
||||
public Animal getAnimal1() {
|
||||
return this.animal1;
|
||||
}
|
||||
|
||||
public Animal getAnimal2() {
|
||||
return this.animal2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class JavaxNamedConstructorArgumentTestBean {
|
||||
|
||||
private final Animal animal1;
|
||||
private final Animal animal2;
|
||||
|
||||
@javax.inject.Inject
|
||||
public JavaxNamedConstructorArgumentTestBean(@javax.inject.Named("Cat") Animal animal1,
|
||||
@javax.inject.Named("Dog") Animal animal2) {
|
||||
|
||||
this.animal1 = animal1;
|
||||
this.animal2 = animal2;
|
||||
}
|
||||
|
||||
public Animal getAnimal1() {
|
||||
return this.animal1;
|
||||
}
|
||||
|
||||
public Animal getAnimal2() {
|
||||
return this.animal2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class QualifiedFieldWithDefaultValueTestBean {
|
||||
|
||||
@Inject
|
||||
@@ -620,6 +689,52 @@ class InjectAnnotationAutowireContextTests {
|
||||
}
|
||||
|
||||
|
||||
interface Animal {
|
||||
|
||||
String getName();
|
||||
}
|
||||
|
||||
|
||||
@jakarta.inject.Named("Cat")
|
||||
static class JakartaCat implements Animal {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Jakarta Tiger";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@javax.inject.Named("Cat")
|
||||
static class JavaxCat implements Animal {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Javax Tiger";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@jakarta.inject.Named("Dog")
|
||||
static class JakartaDog implements Animal {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Jakarta Fido";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@javax.inject.Named("Dog")
|
||||
static class JavaxDog implements Animal {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Javax Fido";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Qualifier
|
||||
|
||||
Reference in New Issue
Block a user