1.概述

在本文中,你将了解Spring框架中不同类型的bean作用域。

bean的范围定义了bean在其使用的上下文中的生命周期和可见性。

Spring 5中定义了6种Scope:

  • singleton
  • prototype
  • request
  • session
  • application
  • websocket

最后的四个只能在web项目里使用。本文只关注前两个。

2. Singleton Scope

使用Singleton Scope定义bean, 意味着容器只创建该bean的单个实例,并且对该bean名称的所有请求将返回缓存的同一对象。对该对象的任何修改都将反映在对bean的所有引用中。如果未指定其他Scope,则此范围是默认值。

让我们创建一个Person实体来举例说明Scope的概念:

public class Person {
    private String name;
 
    public Person() {
    }

    public Person(final String name, final int age) {
        this.name = name;
    }
    // getter and setter
}

然后,我们使用*@Scope*注解定义具有Singleton Scope的bean :

@Bean
@Scope("singleton")
public Person personSingleton() {
    return new Person();
}

我们还可以通过以下方式使用常量而不是String值:

@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)

现在我们继续编写一个测试,测试一下同一个bean的两个对象将具有相同的值,即使其中其中一个更改了状态,因为它们都引用了同一个bean实例:

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = { ScopesConfig.class }, loader = AnnotationConfigContextLoader.class)
public class ScopesTest {
    private static final String NAME = "Test NAME";
    @Autowired
    ApplicationContext context;
    @Test
    public void test_SingletonScope() {
        Person personSingletonA = (Person) context.getBean("personSingleton");
        Person personSingletonB = (Person) context.getBean("personSingleton");

        personSingletonA.setName(NAME);
        assertEquals(NAME, personSingletonB.getName());

        ((AbstractApplicationContext) context).close();
    }
}

我们再来看一下,如果使用XML配置的话,应该怎么写:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="personSingleton" class="com.ripjava.scopes.Person" scope="singleton"/>
</beans>

然后,我们在针对XML写一个测试用例。

private static final String NAME = "Test NAME";

@Test
public void test_SingletonScope() {
  ApplicationContext context = new ClassPathXmlApplicationContext("scopes.xml");
  Person personSingletonA = (Person) context.getBean("personSingleton");
  Person personSingletonB = (Person) context.getBean("personSingleton");
  personSingletonA.setName(NAME);
  assertEquals(NAME, personSingletonB.getName());
  ((AbstractApplicationContext) context).close();
}

3. Prototype Scope

Scope是Prototype 的bean 每次从容器请求时都将返回不同的实例。

@Bean
@Scope("prototype")
public Person personPrototype() {
    return new Person();
}

我们也可以使用常量,就像我们对singleton scope所做的那样:

@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)

我们写一个类似于之前的测试,请求两个相同bean名称的对象,分别设定不同的状态。

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = { ScopesConfig.class }, loader = AnnotationConfigContextLoader.class)
public class ScopesWithAnnotationTest {

    private static final String NAME = "Test NAME";
    private static final String NAME_OTHER = "Test NAME OTHER";
    @Autowired
    ApplicationContext context;

    @Test
    public void test_PrototypeScope() {
        Person personPrototypeA = (Person) context.getBean("personPrototype");
        Person personPrototypeB = (Person) context.getBean("personPrototype");

        personPrototypeA.setName(NAME);
        personPrototypeB.setName(NAME_OTHER);

        assertEquals(NAME, personPrototypeA.getName());
        assertEquals(NAME_OTHER, personPrototypeB.getName());
    }
}

我们再来看一下,如果使用XML配置的话,应该怎么写:

<bean id="personPrototype" class="com.ripjava.scopes.Person" scope="prototype"/>

然后,我们来测试一下

    @Test
    public void test_PrototypeScope() {
        ApplicationContext context = new ClassPathXmlApplicationContext("scopes.xml");
        Person personPrototypeA = (Person) context.getBean("personPrototype");
        Person personPrototypeB = (Person) context.getBean("personPrototype");

        personPrototypeA.setName(NAME);
        personPrototypeB.setName(NAME_OTHER);

        assertEquals(NAME, personPrototypeA.getName());
        assertEquals(NAME_OTHER, personPrototypeB.getName());
        ((AbstractApplicationContext) context).close();
    }

4.结论

在本文中,讨论了Spring提供的singleton,prototype Scope以及它们的用途。

关于Web相关的Scope,等我们写完Spring web相关文章之后再更新。

最后的最后,往常一样,代码可以在Github上找到。