Revise support for JSR-330 and Jakarta @Inject for autowiring test constructors

Closes gh-29851
This commit is contained in:
Sam Brannen
2023-09-08 19:25:31 +02:00
parent 8dd857a84d
commit dfea3d05aa
8 changed files with 117 additions and 191 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 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.
@@ -38,9 +38,10 @@ import org.springframework.lang.Nullable;
* on a test class, the default <em>test constructor autowire mode</em> will be
* used. See {@link #TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME} for details on
* how to change the default mode. Note, however, that a local declaration of
* {@link org.springframework.beans.factory.annotation.Autowired @Autowired} on
* a constructor takes precedence over both {@code @TestConstructor} and the default
* mode.
* {@link org.springframework.beans.factory.annotation.Autowired @Autowired}
* {@link jakarta.inject.Inject @jakarta.inject.Inject}, or
* {@link javax.inject.Inject @javax.inject.Inject} on a constructor takes
* precedence over both {@code @TestConstructor} and the default mode.
*
* <p>This annotation may be used as a <em>meta-annotation</em> to create custom
* <em>composed annotations</em>.
@@ -60,6 +61,8 @@ import org.springframework.lang.Nullable;
* @author Sam Brannen
* @since 5.2
* @see org.springframework.beans.factory.annotation.Autowired @Autowired
* @see jakarta.inject.Inject @jakarta.inject.Inject
* @see javax.inject.Inject @javax.inject.Inject
* @see org.springframework.test.context.junit.jupiter.SpringExtension SpringExtension
* @see org.springframework.test.context.junit.jupiter.SpringJUnitConfig @SpringJUnitConfig
* @see org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig @SpringJUnitWebConfig
@@ -104,6 +107,8 @@ public @interface TestConstructor {
* @return an {@link AutowireMode} to take precedence over the global default
* @see #TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME
* @see org.springframework.beans.factory.annotation.Autowired @Autowired
* @see jakarta.inject.Inject @jakarta.inject.Inject
* @see javax.inject.Inject @javax.inject.Inject
* @see AutowireMode#ALL
* @see AutowireMode#ANNOTATED
*/
@@ -120,7 +125,9 @@ public @interface TestConstructor {
/**
* All test constructor parameters will be autowired as if the constructor
* itself were annotated with
* {@link org.springframework.beans.factory.annotation.Autowired @Autowired}.
* {@link org.springframework.beans.factory.annotation.Autowired @Autowired},
* {@link jakarta.inject.Inject @jakarta.inject.Inject}, or
* {@link javax.inject.Inject @javax.inject.Inject}.
* @see #ANNOTATED
*/
ALL,
@@ -131,7 +138,10 @@ public @interface TestConstructor {
* {@link org.springframework.beans.factory.annotation.Autowired @Autowired},
* {@link org.springframework.beans.factory.annotation.Qualifier @Qualifier},
* or {@link org.springframework.beans.factory.annotation.Value @Value},
* or if the constructor itself is annotated with {@code @Autowired}.
* or if the constructor itself is annotated with
* {@link org.springframework.beans.factory.annotation.Autowired @Autowired},
* {@link jakarta.inject.Inject @jakarta.inject.Inject}, or
* {@link javax.inject.Inject @javax.inject.Inject}.
* @see #ALL
*/
ANNOTATED;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -22,6 +22,9 @@ import java.lang.reflect.Executable;
import java.util.LinkedHashSet;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.SpringProperties;
import org.springframework.core.annotation.AnnotatedElementUtils;
@@ -41,8 +44,37 @@ import org.springframework.util.ClassUtils;
* @since 5.2
* @see TestConstructor
*/
@SuppressWarnings("unchecked")
public abstract class TestConstructorUtils {
private static final Log logger = LogFactory.getLog(TestConstructorUtils.class);
private static final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(2);
static {
autowiredAnnotationTypes.add(Autowired.class);
ClassLoader classLoader = TestConstructorUtils.class.getClassLoader();
try {
autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("jakarta.inject.Inject", classLoader));
logger.trace("'jakarta.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// jakarta.inject API not available - simply skip.
}
try {
autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Inject", classLoader));
logger.trace("'javax.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// javax.inject API not available - simply skip.
}
}
private TestConstructorUtils() {
}
@@ -103,8 +135,9 @@ public abstract class TestConstructorUtils {
* conditions is {@code true}.
*
* <ol>
* <li>The constructor is annotated with {@link Autowired @Autowired}.</li>
* <li>The constructor is annotated with {@link jakarta.inject.Inject} or {@code javax.inject.Inject}.</li>
* <li>The constructor is annotated with {@link Autowired @Autowired},
* {@link jakarta.inject.Inject @jakarta.inject.Inject}, or
* {@link javax.inject.Inject @javax.inject.Inject}.</li>
* <li>{@link TestConstructor @TestConstructor} is <em>present</em> or
* <em>meta-present</em> on the test class with
* {@link TestConstructor#autowireMode() autowireMode} set to
@@ -152,30 +185,9 @@ public abstract class TestConstructorUtils {
return (autowireMode == AutowireMode.ALL);
}
@SuppressWarnings("unchecked")
private static boolean isAnnotatedWithAutowiredOrInject(Constructor<?> constructor) {
Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>();
autowiredAnnotationTypes.add(Autowired.class);
try {
autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("jakarta.inject.Inject", TestConstructorUtils.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
// jakarta.inject API not available - simply skip.
}
try {
autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Inject", TestConstructorUtils.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
// javax.inject API not available - simply skip.
}
return autowiredAnnotationTypes.stream()
.anyMatch(autowiredAnnotationType -> AnnotatedElementUtils.hasAnnotation(constructor, autowiredAnnotationType));
.anyMatch(annotationType -> AnnotatedElementUtils.hasAnnotation(constructor, annotationType));
}
}