Spring中建立帶原型作用域bean5種方法

banq發表於2024-03-29

在這篇簡短的文章中,我們將學習如何在 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);
}

 

相關文章