考察你对 Spring 基本功掌握能力

文摘   2024-11-01 08:00   新疆  

最新实战案例锦集:《Spring Boot3实战案例合集》持续更新,每天至少更新一篇文章,订阅后将赠送文章最后展示的所有MD文档(学习笔记)。

环境:SpringBoot3.2.5



1. 简介

在 Spring Boot 应用程序中,有时需要为同一个类创建多个实例(Bean),并且每个实例可能有不同的配置或用途。虽然可以通过在 @Configuration 配置类中定义多个 @Bean 方法来实现这一点,但在需要创建大量实例的情况下,这种方法不仅冗余,而且难以维护。

在本篇文章中,我们将学习如何在Spring框架中使用注解来创建同一类的多个beans。

2. 实战案例

2.1 使用Java配置

这是使用注解创建多个同类 bean 的最简单易行的方法。在这种方法中,我们将使用基于 Java 的配置类来配置同一类中的多个 Bean,如下示例:

public class Person {  private String name ;  private Integer age ;
  public Person(String name, Integer age) {    this.name = name ;    this.age = age ; } // getters, setters @Override public String toString() {    return "Person [name=" + name+ ", age=" + age+ "]" ; }}

接下来,我们将构建一个名为 PersonConfig 的配置类,并在其中定义 Person 类的多个 Bean:

@Configurationpublic class PersonConfig {  @Bean  public Person person1() {    return new Person("Pack", 22) ;  }
@Bean  public Person person2() {    return new Person("xxgg", 24) ; }}

在这里,@Bean 注解实例化两个bean,并将它们注册到Spring容器中。接下来,我们可以初始化Spring容器,并从Spring容器中请求任何bean。这种策略还使得实现依赖注入变得简单。我们可以使用自动装配直接将一个bean(例如person1)注入到同类型的另一个bean(例如person2)中。

这种方法的局限是,在典型的基于Java的配置风格中,我们需要使用new关键字手动实例化bean。因此,如果相同类的bean数量增加,我们需要先注册它们,并在配置类中创建这些bean。这使得它成为一种更偏向于Java的方法,而不是Spring特有的方法。

2.2 使用@Component注解

在这种方法中,我们将使用@Component注解来创建多个bean,这些bean将从Person类继承属性。首先,我们将创建多个子类,即PersonOne和PersonTwo,它们扩展自Person超类:

@Componentpublic class PersonOne extends Person {
public PersonOne() {    super("Pack", 22) ; }}@Componentpublic class PersonTwo extends Person {
public PersonTwo() { super("xxxooo", 24) ; }}

这种方法的问题是,它不会为同一个类创建多个实例。相反,它创建的是从超类继承属性的类的bean。因此,我们只能在继承类没有定义任何额外属性的情况下使用这种解决方案。此外,使用继承会增加代码的整体复杂性。

2.3 使用BeanFactoryPostProcessor

利用 BeanFactoryPostProcessor 接口的自定义实现来创建同一类的多个 Bean 实例。我们将通过以下步骤来实现:

  • 创建自定义 Bean 类并使用 FactoryBean 接口对其进行配置

  • 使用 BeanFactoryPostProcessor 接口实例化同一类型的多个 Bean

     

自定义 Bean 实现

为了更好地理解这种方法,我们将进一步扩展同一实力。假设有一个 Human 类,它依赖于 Person 类的多个实例:

public class Human implements InitializingBean {
  private Person personOne; private Person personTwo;
@Override public void afterPropertiesSet() throws Exception { Assert.notNull(personOne, "Pack is alive!");    Assert.notNull(personTwo, "Jook is alive!");  } @Autowired public void setPersonOne(Person personOne) { this.personOne = personOne ; this.personOne.setName("Pack") ;    this.personOne.setAge(22) ;  } @Autowired public void setPersonTwo(Person personTwo) { this.personTwo = personTwo ; this.personTwo.setName("Jook") ; this.personTwo.setAge(24) ; }}

InitializingBean 接口会调用 afterPropertiesSet() 方法来检查 BeanFactory 是否设置了所有 Bean 属性,以及是否满足了其他依赖条件。此外,我们将使用setter注入法注入并初始化两个 Person 类 Bean:personOne 和 personTwo。接下来,我们将创建一个实现 FactoryBean 接口的 Person 类。

@Qualifier(value = "personOne, personTwo")public class Person implements FactoryBean<Object> {  private String name ;  private Integer age ;
  public Person() {  } public Class<Person> getObjectType() { return Person.class ;  } public Object getObject() throws Exception { return new Person() ;  } public boolean isSingleton() { return true ;  }}

这里要注意的是 @Qualifier 注解的使用,它包含了类级别上多个 Person 类型的名称或 bean id。在这种情况下,在类级别使用 @Qualifier 是有原因的,我们接下来就会看到。

自定义 BeanFactory 实现

任何实现 BeanFactoryPostProcessor 的类都会在创建任何 Spring Bean 之前执行。BeanFactoryPostProcessor 会扫描所有使用 @Qualifier 注释的类。此外,它还会从注解中提取名称(Bean ID),并用指定的名称手动创建该类类型的实例:

public class PersonFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { Map<String, Object> map = beanFactory.getBeansWithAnnotation(Qualifier.class) ; for (Map.Entry<String, Object> entry : map.entrySet()) {      createInstances(beanFactory, entry.getKey(), entry.getValue()) ; } }
private void createInstances(ConfigurableListableBeanFactory beanFactory, String beanName, Object bean) { Qualifier qualifier = bean.getClass().getAnnotation(Qualifier.class) ; for (String name : extractNames(qualifier)) { Object newBean = beanFactory.getBean(beanName) ; beanFactory.registerSingleton(name.trim(), newBean) ; } }
private String[] extractNames(Qualifier qualifier) { return qualifier.value().split(",") ; }}

在这里,自定义 BeanFactoryPostProcessor 实现会在 Spring 容器初始化后被调用。

最后,配置上面定义的Human及BeanFactoryPostProcessor

@Configurationpublic class PersonConfig {  @Bean  public PersonFactoryPostProcessor PersonFactoryPostProcessor() {    return new PersonFactoryPostProcessor();  }  @Bean  public Person person() {    return new Person() ;  }  @Bean  public Human human() {    return new Human() ;  }}

这种方法的局限性在于其复杂性。所以不鼓励使用这种方法。尽管有其局限性,但这种方法更符合 Spring 的特性,可以使用注解实例化多个相似类型的 Bean。

以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏

推荐文章

虚拟线程在Spring Boot中的应用及性能对比

优雅!通过编程方式重启SpringBoot应用的3种方案

强大!SpringBoot通过插件,动态扩展系统功能

强大!基于Spring Boot动态注册 / 删除Controller接口(支持动态上传)

弃用RestTemplate!RestClient真香

彻底搞懂Spring Boot3才有的ProblemDetail和ErrorResponse

新功能来了!Spring非常实用的5个功能

一文彻底玩转@RequestMapping,高级用法你未必知道

Spring6.1 异步和定时任务新特性,太实用了

SpringBoot请求参数还可以这样玩?很少有人知道

分享几个你不知道的高级SpringBoot最佳实践

强大!SpringBoot结合STOMP简化数据实时通信

总结了8个SpringBoot开发技巧,你都知道吗?

Spring强大的FactoryBean还能这样用,涨知识

当心!请不要在SpringBoot中再犯这样严重的错误

Spring全家桶实战案例源码
spring, springboot, springcloud 案例开发详解
 最新文章