Spring @Autowired 在测试中的行为与组件不同
Spring @Autowired behavior different in tests than components
写测试的时候 rules/behaviors 和 @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() {
}
}
}
运行良好的测试:
@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles(Application.Profiles.NO_DEPENDENCIES)
public class TestClass {
@Autowired
private GizmoConfiguration.FooGizmo gizmo;
@Test
public void test() {
assertNotNull(gizmo);
}
}
导致 java.lang.IllegalStateException: Failed to load ApplicationContext
的组件:
@Component
public class TestComponent {
@Autowired
private GizmoConfiguration.FooGizmo gizmo;
}
因为:
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)}
Are the rules/behaviors around @Autowired different when writing
tests?
不完全相同:规则实际上完全相同。不同之处在于 Spring 如何确定给定的 bean 是否是自动装配候选者的时间。
It seems that with a test, you can autowire to a concrete type,
but if you try the same thing inside a @Component it will fail.
我理解你为什么会这么想,因为你的例子证明了这种行为,但你的分析并不完全正确。
所以让我解释一下...
当 Spring 尝试为您的 @Component
class 执行自动装配时,它所拥有的关于即将到来的 bean 的类型(即 classes 和接口)的唯一信息来自 @Bean
方法的信息是 @Bean
方法的正式签名中可用的信息。
在你的例子中,当Spring搜索所谓的"autowire candidates"注入你的@Component
时,Spring只看到[=14=类型的bean ] 用于您的 fooGizmoBean()
@Bean
方法。这就是为什么您会看到 "No qualifying bean of type 'GizmoConfiguration$FooGizmo'" 错误,而这个错误恰好是完全正确的。
如果您希望 Spring 能够使用具体类型自动装配您的 @Component
,您将必须重新定义 fooGizmoBean()
@Bean
方法的签名以return FooGizmo
而不是 Gizmo
.
所以,这就是故事的前半部分。故事的后半部分是为什么 Spring TestContext Framework 能够通过测试实例的具体类型执行自动装配。
有效的原因是 ApplicationContext
在测试时已经完全启动(即,所有 bean 都已实例化并且容器已调用所有 @Bean
方法)框架尝试执行依赖注入。到那时,fooGizmoBean()
方法已经被 Spring 调用,并且 Spring 现在知道具体类型实际上是 FooGizmo
。因此,@Autowired FooGizmo gizmo;
在测试中有效。
写测试的时候 rules/behaviors 和 @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() {
}
}
}
运行良好的测试:
@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles(Application.Profiles.NO_DEPENDENCIES)
public class TestClass {
@Autowired
private GizmoConfiguration.FooGizmo gizmo;
@Test
public void test() {
assertNotNull(gizmo);
}
}
导致 java.lang.IllegalStateException: Failed to load ApplicationContext
的组件:
@Component
public class TestComponent {
@Autowired
private GizmoConfiguration.FooGizmo gizmo;
}
因为:
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)}
Are the rules/behaviors around @Autowired different when writing tests?
不完全相同:规则实际上完全相同。不同之处在于 Spring 如何确定给定的 bean 是否是自动装配候选者的时间。
It seems that with a test, you can autowire to a concrete type, but if you try the same thing inside a @Component it will fail.
我理解你为什么会这么想,因为你的例子证明了这种行为,但你的分析并不完全正确。
所以让我解释一下...
当 Spring 尝试为您的 @Component
class 执行自动装配时,它所拥有的关于即将到来的 bean 的类型(即 classes 和接口)的唯一信息来自 @Bean
方法的信息是 @Bean
方法的正式签名中可用的信息。
在你的例子中,当Spring搜索所谓的"autowire candidates"注入你的@Component
时,Spring只看到[=14=类型的bean ] 用于您的 fooGizmoBean()
@Bean
方法。这就是为什么您会看到 "No qualifying bean of type 'GizmoConfiguration$FooGizmo'" 错误,而这个错误恰好是完全正确的。
如果您希望 Spring 能够使用具体类型自动装配您的 @Component
,您将必须重新定义 fooGizmoBean()
@Bean
方法的签名以return FooGizmo
而不是 Gizmo
.
所以,这就是故事的前半部分。故事的后半部分是为什么 Spring TestContext Framework 能够通过测试实例的具体类型执行自动装配。
有效的原因是 ApplicationContext
在测试时已经完全启动(即,所有 bean 都已实例化并且容器已调用所有 @Bean
方法)框架尝试执行依赖注入。到那时,fooGizmoBean()
方法已经被 Spring 调用,并且 Spring 现在知道具体类型实际上是 FooGizmo
。因此,@Autowired FooGizmo gizmo;
在测试中有效。