1、概览 {#1概览}
本文将带你了解如何在 Spring 中使用运行时参数创建一个 Prototype Scope Bean。
在 Spring 中,有许多不同的 Bean Scope,默认的 Scope 是 Singleton(单例)。Singleton Scope 的 Bean 将始终产生相同的对象。
如果每次都需要从容器中获得一个新实例,可以使用 Pototype Scope Bean。然而,在大多数情况下,如果我们想要从一个 Singleton Bean 实例化 Pototype Bean,或者将动态参数传递给 Pototype Bean,可能会遇到一些问题。
2、使用动态参数创建 Prototype Bean {#2使用动态参数创建-prototype--bean}
有时,我们需要在每次初始化时将动态参数作为输入来初始化 Spring Bean。通过 Spring,可以使用多种方法为 Prototype Bean 分配不同的动态参数。
首先,创建一个 Prototype Bean Employee
:
public class Employee {
private String name;
public Employee(String name) {
this.name = name;
}
public void printName() {
System.out.println(name);
}
}
为 Employee
Prototype Bean 创建一个配置:
@Configuration
public class EmployeeConfig {
@Bean(name = "Employee")
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public Employee createPrototype(String name) {
return new Employee(name);
}
}
2.1、使用 Application Context {#21使用-application-context}
一般来说,使用 ApplicationContext
获取 Prototype 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.2、使用工厂方法 {#22使用工厂方法}
Spring 提供了 ObjectFactory<T>
接口,用于按需生成给定类型的对象。
使用 ObjectFactory
为 Employee
Bean 创建一个 EmployeeFactory
:
public class EmployeeBeanUsingObjectFactory {
@Autowired
private ObjectFactory employeeObjectFactory;
public Employee getEmployee() {
return employeeObjectFactory.getObject();
}
}
如上,每调用一次 getEmployee()
,Spring 就会返回一个新的 Employee
对象。
2.3、使用 @Lookup {#23使用-lookup}
使用 @Lookup
注解注入方法也能解决问题。使用 @Lookup
注解的任何方法都将被 Spring 容器覆写,然后 Spring 容器将返回该方法的命名 Bean。
创建一个组件,并创建一个带有 @Lookup
注解的方法来获取 Employee
对象:
@Component
public class EmployeeBeanUsingLookUp {
@Lookup
public Employee getEmployee(String arg) {
return null;
}
}
注解了 @Lookup
的方法(如 getEmployee()
)将被 Spring 覆写。因此,Bean 会在 Application Context 中注册。每次调用 getEmployee()
方法时,都会返回一个新的 Employee
实例。
Spring 使用 CGLIB 生成字节码,类和方法都不能是 final
的。
测试给定 Prototype 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);
}
2.4、使用 Function {#24使用-function}
Spring 提供了另一个选项 Function
,用于在运行时创建 Prototype Bean。我们还可以将参数应用于新创建的 Prototype 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);
}
2.5、使用 ObjectProvider {#25使用-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);
}
3、总结 {#3总结}
本文介绍了如何在 Spring 中动态创建地创建 Prototype Scope Bean。
Ref:https://www.baeldung.com/spring-prototype-bean-runtime-arguments