在这篇简短的文章中,我们将学习如何在 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); }
|