1、概览 {#1概览}
当有多个相同类型的实现时,需要对 Spring Bean 进行不同的命名。这是因为如果 Bean 没有唯一的名称,Spring 在注入 Bean 时会出现歧义。
通过控制 Bean 的命名,可以告诉 Spring 我们想将哪个 Bean 注入到目标对象中。
本文将带你了解 Spring Bean 命名策略,以及如何为同一类型的 Bean 赋予多个名称。
2、默认 Bean 命名策略 {#2默认-bean-命名策略}
Spring 为创建 Bean 提供了多种注解,可以在不同的级别使用。例如,可以在 Bean 类上放置一些注解,在创建 Bean 的方法上放置另一些注解。
首先,来看看 Spring 的默认命名策略。当只指定注解而不指定任何值时,Spring 是如何命名 Bean 的?
2.1、类级注解 {#21类级注解}
首先从在类级别使用的注解的默认命名策略开始。Spring 会使用类名为 Bean 命名,并将第一个字母转换为小写。
举个例子:
@Service
public class LoggingService {
}
如上,Spring 为 LoggingService
类创建了一个 Bean,并使用 loggingService
名称进行了注册。
这种默认的命名策略适用于所有用于创建 Spring Bean 的类级别注解,例如 @Component
、@Service
和 @Controller
。
2.2、方法级注解 {#22方法级注解}
Spring 提供了 @Bean
和 @Qualifier
等注解,可用于创建 Bean 的方法。
让我们通过一个示例来了解 @Bean
注解的默认命名策略:
@Configuration
public class AuditConfiguration {
@Bean
public AuditService audit() {
return new AuditService();
}
}
在这个配置类中,Spring 以 audit
为名注册了一个 AuditService
类型的 Bean。当在方法上使用 @Bean
注解时,Spring 会将方法名用作 Bean 名。
还可以在方法上使用 @Qualifier
注解,如下。
3、自定义 Bean 的命名 {#3自定义-bean-的命名}
当需要在同一个 Spring Context 中创建多个相同类型的 Bean 时,可以为 Bean 自定义名称,并使用这些名称来引用它们。
来看看如何为 Spring Bean 自定义名称:
@Component("myBean")
public class MyCustomComponent {
}
这一次,Spring 将创建 MyCustomComponent
类型的 Bean,并命名为 myBean
。
由于我们显式地给 Bean 赋予了名称,Spring 将使用这个名称,然后就可以用它来引用或访问 Bean 了。
与 @Component("myBean")
类似,也可以使用 @Service("myService")
、@Controller("myController")
和 @Bean("myCustomBean")
等其他注解指定名称,然后 Spring 将以给定的名称注册 Bean。
4、使用 @Bean 和 @Qualifier 命名 Bean {#4使用-bean-和-qualifier-命名-bean}
4.1、@Bean 的 value 值 {#41bean-的-value-值}
如前所述,@Bean
注解应用于方法级别,默认情况下,Spring 将方法名称用作 Bean 名称。
这个默认的 Bean 名称可以被覆盖 - 可以使用 @Bean
注解来指定值。
@Configuration
public class MyConfiguration {
@Bean("beanComponent")
public MyCustomComponent myComponent() {
return new MyCustomComponent();
}
}
此时,当我们要获取 MyCustomComponent
类型的 Bean 时,可以使用 beanComponent
这个名称来引用该 Bean。
Spring @Bean
注解通常在配置类方法中声明。它可以通过直接调用同一类中的其他 @Bean
方法来引用它们。
4.2、@Qualifier 的 value 值 {#42qualifier-的-value-值}
还可以使用 @Qualifier
注解来命名 Bean。
首先,创建一个接口 Animal
,它将会由多个类实现:
public interface Animal {
String name();
}
现在,定义一个实现类 Cat
,并为其添加 @Qualifier
注解,其 value 值为 cat
:
@Component
@Qualifier("cat")
public class Cat implements Animal {
@Override
public String name() {
return "Cat";
}
}
再添加一个 Animal
的实现 Dog
,并用 value 值为 dog
的 @Qualifier
对其进行注解:
@Component
@Qualifier("dog")
public class Dog implements Animal {
@Override
public String name() {
return "Dog";
}
}
现在,编写一个 PetShow
类,在其中注入两个不同的 Animal
实例:
@Service
public class PetShow {
private final Animal dog;
private final Animal cat;
public PetShow (@Qualifier("dog")Animal dog, @Qualifier("cat")Animal cat) {
this.dog = dog;
this.cat = cat;
}
public Animal getDog() {
return dog;
}
public Animal getCat() {
return cat;
}
}
在 PetShow
类中,通过在构造函数参数上使用 @Qualifier
注解,并在每个注解的 value
属性中使用限定的 Bean 名称,注入了 Animal
类型的两个实现。每当我们使用该限定名称时,Spring 就会将具有该限定名称的 Bean 注入到目标 Bean 中。
5、验证 Bean 名称 {#5验证-bean-名称}
到目前为止,我们已经看到了为 Spring Bean 命名的不同示例。现在的问题是,我们如何验证或测试这一点?
可以通过一个单元测试来验证这种行为:
@ExtendWith(SpringExtension.class)
public class SpringBeanNamingUnitTest {
private AnnotationConfigApplicationContext context;
@BeforeEach
void setUp() {
context = new AnnotationConfigApplicationContext();
context.scan("com.baeldung.springbean.naming");
context.refresh();
}
@Test
void givenMultipleImplementationsOfAnimal_whenFieldIsInjectedWithQualifiedName_thenTheSpecificBeanShouldGetInjected() {
PetShow petShow = (PetShow) context.getBean("petShow");
assertThat(petShow.getCat().getClass()).isEqualTo(Cat.class);
assertThat(petShow.getDog().getClass()).isEqualTo(Dog.class);
}
在此 JUnit 测试中,在 setUp
方法中初始化了 AnnotationConfigApplicationContext
,该方法用于获取 Bean。
然后,只需使用标准断言验证 Spring Bean 的类。
6、总结 {#6总结}
本文介绍了 Spring 中 Bean 默认的命令策略,以及如何自定义 Bean 的命名。还介绍了如何为同一类型的 Bean 赋予多个名称。
Ref:https://www.baeldung.com/spring-bean-names