1. 概述

当我们有同一类型的多个实现时,命名Spring bean非常有用。这是因为,如果我们的 bean 没有唯一的名称,Spring 注入 bean 会变得不明确。

通过控制 bean 的命名,我们可以告诉 Spring 我们想要将哪个 bean 注入到目标对象中。

在本文中,我们将讨论 Spring bean 命名策略,并探讨如何为单一类型的 bean 提供多个名称。

2.默认Bean命名策略

Spring 提供了多个用于创建 bean 的注释。我们可以在不同级别使用这些注释。例如,我们可以在 bean 类上放置一些注释,而在创建 bean 的方法上放置其他注释。

首先,让我们看看 Spring 的默认命名策略是如何运作的。当我们只指定注释而没有任何值时,Spring 如何命名我们的 bean?

2.1.类级注释

让我们从类级别使用的注释的默认命名策略开始。为了命名一个 bean, Spring 使用类名并将第一个字母转换为小写

让我们看一个例子:

@Service
public class LoggingService {
}

在这里,Spring 为 LoggingService 类创建一个 bean,并使用名称“ loggingService ”注册它。

同样的默认命名策略适用于用于创建 Spring bean 的所有类级注释,例如 @Component@Service@Controller

2.2.方法级注释

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的自定义命名

当我们需要在同一个 Spring 上下文中创建多个相同类型的 bean 时,我们可以为这些 bean 指定自定义名称,并使用这些名称来引用它们。

那么,让我们看看如何为 Spring bean 指定一个自定义名称:

@Component("myBean")
public class MyCustomComponent {
}

这次,Spring 将创建类型为 MyCustomComponent 且名称为“ myBean ”的 bean。

由于我们显式地为 bean 指定了名称,Spring 将使用该名称,然后可以使用该名称来引用或访问该 bean。

@Component(“myBean”) 类似,我们可以使用 @Service(“myService”)@Controller(“myController”)@Bean(“myCustomBean”) 等其他注解来指定名称,然后Spring会注册具有给定名称的豆。

4. 使用 @Bean@Qualifier 命名Bean

4.1. @Bean 有价值

正如我们之前看到的, @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 具有价值

我们还可以使用 @Qualifier 注解来命名bean。

首先,让我们创建一个将由多个类实现的接口 Animal

public interface Animal {
    String name();
}

现在,让我们定义一个实现类 Cat 并为其添加 @Qualifier 注解,值为“ cat ”:

@Component 
@Qualifier("cat") 
public class Cat implements Animal { 
    @Override 
     public String name() { 
        return "Cat"; 
     } 
}

让我们添加 Animal 的另一个实现,并使用 @Qualifier 和值“ dog ”对其进行注释:

@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; 
    }
}

在类 Pet**Show 中, 我们通过在构造函数参数上使用 @Qualifier 注释来注入 Animal 类型的两种实现,并在每个注释的 value 属性中使用限定的 bean 名称。每当我们使用这个限定名称时,Spring 都会将具有该限定名称的 bean 注入到目标 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 的类。

六,结论

在这篇简短的文章中,我们研究了默认和自定义 Spring bean 命名策略。

我们还了解了自定义 Spring bean 命名在需要管理多个相同类型的 bean 的用例中如何有用。

与往常一样,本文的完整代码可以在 GitHub 上找到。