1. 概述
在这篇简短的文章中,我们将学习如何在Spring中使用运行时参数创建原型作用域的bean。
在Spring中,有多种不同的bean作用域,但默认的是单例作用域,这意味着单例作用域的bean始终会返回同一个对象。然而,如果我们需要容器每次请求时都生成一个新的实例,我们可以使用原型作用域的bean。但在大多数情况下,如果试图从单例bean实例化原型或将动态参数传递给原型bean,我们可能会遇到问题。
Spring提供了多种方法来实现这些目标,接下来我们将深入探讨这些方法。
2. 使用动态参数创建原型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);
}
}
2.1. 使用ApplicationContext
通常,这是通过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.2. 使用工厂方法
Spring提供了*ObjectFactory
让我们使用ObjectFactory为Employee bean创建一个EmployeeFactory:
public class EmployeeBeanUsingObjectFactory {
@Autowired
private ObjectFactory employeeObjectFactory;
public Employee getEmployee() {
return employeeObjectFactory.getObject();
}
}
每当调用getEmployee()时,Spring都会返回一个新的Employee对象。
2.3. 使用@Lookup
另一种方法是使用*@Lookup注解进行方法注入。任何带有@Lookup*注解的方法都将被Spring容器覆盖,然后返回该方法对应的命名bean。
让我们创建一个组件,并为获取Employee对象的方法添加*@Lookup*注解:
@Component
public class EmployeeBeanUsingLookUp {
@Lookup
public Employee getEmployee(String arg) {
return null;
}
}
带有*@Lookup注解的方法,如getEmployee(),将被Spring容器重写。因此,bean会在应用程序上下文中注册。每次调用getEmployee()方法时,都会返回一个新的Employee*实例。
Spring将使用CGLIB生成字节码,类和方法都不能是final。
现在,让我们测试*@Lookup*方法,对于给定的原型bean,检查它是否返回不同的实例:
@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
Spring还提供了另一个选项,即Function,用于在运行时创建原型bean。我们还可以将参数应用到新创建的原型bean实例上。
首先,我们使用Function创建一个组件,其中name字段将被添加到实例中:
@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
Spring提供了ObjectProvider
让我们注入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. 总结
在这篇简短教程中,我们学习了在Spring中动态创建原型作用域bean的几种方法。
如往常一样,本教程的完整代码可以在GitHub上找到。