Spring中创建带原型作用域bean5种方法

在这篇简短的文章中,我们将学习如何在 Spring 中创建带有运行时参数的原型作用域(prototype-scoped) bean。

在Spring中,有许多不同的bean作用域,但默认作用域是单例,这意味着单例作用域的bean将始终生成相同的对象。

或者,如果每次都需要来自容器的新实例,我们可以使用原型范围的 bean。然而,在大多数情况下,如果我们想要从单例 bean 实例化原型或将动态参数传输到原型 bean,我们会遇到问题。

Spring 提供了许多方法来实现这些目标,我们将在本教程中深入讨论。

使用动态参数创建原型 Bean
有时我们需要在每次初始化时使用动态参数作为输入来初始化 Spring bean。可以使用多种方法通过 Spring 为原型 bean 分配不同的动态参数。

我们将一一分析它们,看看它们的优点和缺点。

首先,我们先创建一个原型 bean Employee:

public class Employee {
    private String name;
    public Employee(String name) {
        this.name = name;
    }
    public void printName() {
        System.out.println(name);
    }
}

另外,让我们为Employee原型 bean 创建一个配置:

@Configuration
public class EmployeeConfig {
    @Bean(name = "Employee")
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    public Employee createPrototype(String name) {
        return new Employee(name);
    }
}

1.使用应用程序上下文
一般来说,这是使用ApplicationContext获取原型 bean 的最基本、最简单的方法。

让我们将ApplicationContext注入到我们的组件中:

@Component
public class UseEmployeePrototype {
    private ApplicationContext applicationContext;
    @Autowired
    public UseEmployeePrototype(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
    public void usePrototype() {
        Employee employee = (Employee) applicationContext.getBean("Employee", "sachin");
        employee.printName();
    }
}

正如我们在这里看到的,我们将 bean 创建与 ApplicationContext 紧密耦合。因此,如果我们更改 bean 实现,该方法可能会受到影响。

2.使用工厂方法
Spring 提供了 ObjectFactory<T> 接口来按需生成给定类型的对象。

让我们使用ObjectFactory为Employee bean 创建一个EmployeeFactory:

public class EmployeeBeanUsingObjectFactory {
    @Autowired
    private ObjectFactory employeeObjectFactory;
    public Employee getEmployee() {
        return employeeObjectFactory.getObject();
    }
}

在这里,每次调用getEmployee()时,Spring 都会返回一个新的Employee对象。

3.使用@Lookup
或者,使用@Lookup 注释的方法注入可以解决该问题。我们使用@Lookup注释注入的任何方法都将被Spring容器覆盖,然后Spring容器将返回该方法的命名bean。

让我们创建一个组件并创建一个带有@Lookup注解的方法来获取Employee对象:

@Component
public class EmployeeBeanUsingLookUp {
    @Lookup
    public Employee getEmployee(String arg) {
        return null;
    }
}

用@Lookup注解的方法,例如getEmployee (),将被 Spring 覆盖。结果,bean 被注册到应用程序上下文中。每次调用getEmployee () 方法时都会返回一个新的Employee实例。

Spring将使用CGLIB生成字节码,并且类和方法都不能是final的。

现在,让我们测试给定原型 bean 的@Lookup方法并检查它是否返回不同的实例:

@Test
public void givenPrototypeBean_WhenLookup_ThenNewInstanceReturn() {
    AbstractApplicationContext context = new AnnotationConfigApplicationContext(EmployeeConfig.class);
    EmployeeBeanUsingLookUp firstContext = context.getBean(EmployeeBeanUsingLookUp.class);
    EmployeeBeanUsingLookUp secondContext = context.getBean(EmployeeBeanUsingLookUp.class);
    Employee firstInstance = firstContext.getEmployee("sachin");
    Employee secondInstance = secondContext.getEmployee(
"kumar");
    Assert.assertTrue(firstInstance != secondInstance);
}

4.使用Function
Spring 提供了另一个选项Function,用于在运行时创建原型 bean。我们还可以将参数应用于新创建的原型 bean 实例。

首先,让我们使用Function创建一个组件,其中名称字段将添加到实例中:

@Component
public class EmployeeBeanUsingFunction {
    @Autowired
    private Function<String, Employee> beanFactory;
    public Employee getEmployee(String name) {
        Employee employee = beanFactory.apply(name);
        return employee;
    }
}

此外,现在让我们在 bean 配置中添加一个新的beanFactory() :

@Configuration
public class EmployeeConfig {
    @Bean
    @Scope(value = "prototype")
    public Employee getEmployee(String name) {
        return new Employee(name);
    }
    @Bean
    public Function<String, Employee> beanFactory() {
        return name -> getEmployee(name);
    }
}

最后,我们将检查实例是否不同:

@Test
public void givenPrototypeBean_WhenFunction_ThenNewInstanceReturn() {
    AbstractApplicationContext context = new AnnotationConfigApplicationContext(EmployeeConfig.class);
    EmployeeBeanUsingFunction firstContext = context.getBean(EmployeeBeanUsingFunction.class);
    EmployeeBeanUsingFunction secondContext = context.getBean(EmployeeBeanUsingFunction.class);
    Employee firstInstance = firstContext.getEmployee("sachin");
    Employee secondInstance = secondContext.getEmployee(
"kumar");
    Assert.assertTrue(firstInstance != secondInstance);
}

5.使用ObjectProvider
Spring 提供了ObjectProvider<T> ,它是现有ObjectFactory接口的扩展。

让我们注入 ObjectProvider并使用ObjectProvider获取Employee对象:

public class EmployeeBeanUsingObjectProvider {
    @Autowired
    private org.springframework.beans.factory.ObjectProvider objectProvider;
    public Employee getEmployee(String name) {
        Employee employee = objectProvider.getObject(name);
        return employee;
    }
}

现在,让我们测试并检查实例是否不同:

@Test
public void givenPrototypeBean_WhenObjectProvider_ThenNewInstanceReturn() {
    AbstractApplicationContext context = new AnnotationConfigApplicationContext(EmployeeConfig.class);
    EmployeeBeanUsingObjectProvider firstContext = context.getBean(EmployeeBeanUsingObjectProvider.class);
    EmployeeBeanUsingObjectProvider secondContext = context.getBean(EmployeeBeanUsingObjectProvider.class);
    Employee firstInstance = firstContext.getEmployee("sachin");
    Employee secondInstance = secondContext.getEmployee(
"kumar");
    Assert.assertTrue(firstInstance != secondInstance);
}