为什么@SpringBootTest在构造函数注入中需要@Autowired

Why does @SpringBootTest need @Autowired in constructor injection

一个更笼统的问题。如果在常规 Spring 托管 class 中使用构造函数注入,则 classes 无需 @Autowired 注释即可自动装配,即。即:

@Service
class MailService(
  private val projectService: ProjectService,
  private val mailer: Mailer
) { ... }

在@SpringBootTest class中遵循相同的构造函数注入原则,您需要将@Autowired注解设置为构造函数参数,否则将无法注入class, 一世。即:

@SpringBootTest
internal class MailerTest(
  @Autowired private val mailer: Mailer
) { ... }

为什么会出现这种差异?

在 Spring 启动应用程序的情况下,spring 负责连接 bean。

对于 JUnit 5,由 Spring 管理的 Beans 必须注入由 JUnit 管理的测试实例 class。幸运的是,JUnit 5 提供了一种通过 ParameterResolver.

来做到这一点的方法

@SpringBootTest 注册 SpringExtension,除其他功能外,它还用作 ParameterResolver:

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
    Parameter parameter = parameterContext.getParameter();
    Executable executable = parameter.getDeclaringExecutable();
    Class<?> testClass = extensionContext.getRequiredTestClass();
    PropertyProvider junitPropertyProvider = propertyName ->
    extensionContext.getConfigurationParameter(propertyName).orElse(null);
    return (TestConstructorUtils.isAutowirableConstructor(executable, testClass, junitPropertyProvider) ||
            ApplicationContext.class.isAssignableFrom(parameter.getType()) ||
            supportsApplicationEvents(parameterContext) ||
            ParameterResolutionDelegate.isAutowirable(parameter, parameterContext.getIndex()));
}

ParameterResolutionDelegate.isAutowirable 依靠注解来确定是否可以从 Spring 的 ApplicationContext

中注入参数
public static boolean isAutowirable(Parameter parameter, int parameterIndex) {
    Assert.notNull(parameter, "Parameter must not be null");
    AnnotatedElement annotatedParameter = getEffectiveAnnotatedParameter(parameter, parameterIndex);
    return (AnnotatedElementUtils.hasAnnotation(annotatedParameter, Autowired.class) ||
            AnnotatedElementUtils.hasAnnotation(annotatedParameter, Qualifier.class) ||
            AnnotatedElementUtils.hasAnnotation(annotatedParameter, Value.class));
}

事实上,如果省略@Autowired注解,JUnit会抱怨缺少ParameterResolver:

org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [test.Mailer mailer] in constructor [public test.MailServiceTest(test.Mailer)].