Spring @Autowired行为在测试中与组件不同

chi*_*fet 6 spring spring-test spring-ioc

@Autowired在编写测试时,规则/行为是否不同?似乎通过测试,您可以自动装配到具体类型,但如果您在其中尝试相同的事情@Component,它将失败.这是一个人为的例子,但这是我遇到的事情,我只是想更好地理解.

受控示例代码:

public interface Gizmo {

  void whirr();
}

@Configuration
public class GizmoConfiguration {

  @Bean
  @Profile("no-dependencies")
  public Gizmo fooGizmoBean() {
    return new FooGizmo();
  }

  @Bean
  @Profile("!no-dependencies")
  public Gizmo barGizmoBean() {
    return new BarGizmo();
  }

  public class FooGizmo implements Gizmo {
    @Override
    public void whirr() {
    }
  }

  public class BarGizmo implements Gizmo {
    @Override
    public void whirr() {
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

运行良好的测试:

@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles(Application.Profiles.NO_DEPENDENCIES)
public class TestClass {

  @Autowired
  private GizmoConfiguration.FooGizmo gizmo;

  @Test
  public void test() {
    assertNotNull(gizmo);
  }
}
Run Code Online (Sandbox Code Playgroud)

导致的组件java.lang.IllegalStateException: Failed to load ApplicationContext:

@Component
public class TestComponent {

  @Autowired
  private GizmoConfiguration.FooGizmo gizmo;
}
Run Code Online (Sandbox Code Playgroud)

因为:

No qualifying bean of type 'GizmoConfiguration$FooGizmo' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
Run Code Online (Sandbox Code Playgroud)

Sam*_*nen 13

编写测试时,围绕@Autowired的规则/行为是否有所不同?

不完全是:规则实际上完全一样.关于Spring如何确定给定bean是否为autowire候选者的时间方面的区别.

似乎通过测试,您可以自动装配到具体类型,但如果您在@Component中尝试相同的事情,它将失败.

我明白为什么你会这么想,因为你的例子证明了这种行为,但你的分析并不完全正确.

那么让我解释一下......

当Spring尝试为您的@Component类执行自动装配时,它对来自@Bean方法的bean的类型(即类和接口)的唯一信息是@Bean方法的正式签名中可用的信息.

在你的例子中,当Spring搜索所谓的"autowire candidate"以注入你的时候@Component,Spring只会Gizmo为你的fooGizmoBean() @Bean方法看到一个类型的bean .这就是为什么你看到"没有类型'GizmoConfiguration $ FooGizmo'的限定bean"错误,这恰好是完全正确的.

如果您希望Spring能够@Component使用具体类型自动装配您,则必须重新定义fooGizmoBean() @Bean方法的签名FooGizmo而不是返回Gizmo.

所以,这就是故事的前半部分.故事的后半部分是Spring TestContext Framework能够通过测试实例的具体类型执行自动装配的原因.

可行的原因是,在测试框架尝试执行依赖项注入时ApplicationContext,已经完全启动(即,所有bean已经实例化并且所有@Bean方法都已被容器调用).到那个时候,fooGizmoBean()Spring已经调用了这个方法,Spring现在知道具体类型实际上是一个FooGizmo.因此,@Autowired FooGizmo gizmo;在测试中工作.