在這篇簡短的文章中,我們將學習如何在 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 = <font>"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(<font>"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(<font>"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 = <font>"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(<font>"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(<font>"sachin"); Employee secondInstance = secondContext.getEmployee("kumar"); Assert.assertTrue(firstInstance != secondInstance); }
|