Quick Guide to Spring Bean Scopes – Spring Bean 的作用域快速指南

最后修改: 2016年 6月 3日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this quick tutorial, we’ll learn about the different types of bean scopes in the Spring framework.

在这个快速教程中,我们将学习Spring框架中不同类型的bean作用域。

The scope of a bean defines the life cycle and visibility of that bean in the contexts we use it.

一个Bean的范围定义了该Bean在我们使用它的情况下的生命周期和可见性。

The latest version of the Spring framework defines 6 types of scopes:

最新版本的Spring框架定义了6种作用域。

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

The last four scopes mentioned, request, session, application and websocket, are only available in a web-aware application.

最后提到的四个作用域,request、session、applicationwebsocket,只有在Web感知的应用程序中才可用。

2. Singleton Scope

2.单子范围

When we define a bean with the singleton scope, the container creates a single instance of that bean; all requests for that bean name will return the same object, which is cached. Any modifications to the object will be reflected in all references to the bean. This scope is the default value if no other scope is specified.

当我们用 singleton 作用域定义 bean 时,容器会创建该 bean 的单个实例;对该 bean 名称的所有请求将返回相同的对象,该对象被缓存起来。对该对象的任何修改都将反映在对该 bean 的所有引用中。如果没有指定其它作用域,这个作用域是默认值。

Let’s create a Person entity to exemplify the concept of scopes:

让我们创建一个Person实体来示范作用域的概念。

public class Person {
    private String name;

    // standard constructor, getters and setters
}

Afterwards, we define the bean with the singleton scope by using the @Scope annotation:

之后,我们通过使用@Scope注解,用singleton范围定义Bean。

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

We can also use a constant instead of the String value in the following manner:

我们也可以用一个常数来代替String值,方式如下。

@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)

Now we can proceed to write a test that shows that two objects referring to the same bean will have the same values, even if only one of them changes their state, as they are both referencing the same bean instance:

现在我们可以继续写一个测试,表明两个引用同一bean的对象将有相同的值,即使只有一个对象改变了它们的状态,因为它们都引用了同一个bean实例。

private static final String NAME = "John Smith";

@Test
public void givenSingletonScope_whenSetName_thenEqualNames() {
    ApplicationContext applicationContext = 
      new ClassPathXmlApplicationContext("scopes.xml");

    Person personSingletonA = (Person) applicationContext.getBean("personSingleton");
    Person personSingletonB = (Person) applicationContext.getBean("personSingleton");

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

    ((AbstractApplicationContext) applicationContext).close();
}

The scopes.xml file in this example should contain the xml definitions of the beans used:

本例中的scopes.xml文件应该包含所用Bean的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="org.baeldung.scopes.Person" scope="singleton"/>    
</beans>

3. Prototype Scope

3.原型的范围

A bean with the prototype scope will return a different instance every time it is requested from the container. It is defined by setting the value prototype to the @Scope annotation in the bean definition:

具有 prototype 作用域的 bean 将在每次从容器中请求它时返回一个不同的实例。它是通过将prototype值设置为Bean定义中的@Scope注解来定义的。

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

We can also use a constant like we did for the singleton scope:

我们也可以像对singleton作用域那样使用一个常量。

@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)

We will now write a similar test as before that shows two objects requesting the same bean name with the prototype scope. They will have different states as they are no longer referring to the same bean instance:

我们现在将写一个与之前类似的测试,显示两个对象以原型范围请求相同的Bean名称。它们将有不同的状态,因为它们不再是指同一个Bean实例。

private static final String NAME = "John Smith";
private static final String NAME_OTHER = "Anna Jones";

@Test
public void givenPrototypeScope_whenSetNames_thenDifferentNames() {
    ApplicationContext applicationContext = 
      new ClassPathXmlApplicationContext("scopes.xml");

    Person personPrototypeA = (Person) applicationContext.getBean("personPrototype");
    Person personPrototypeB = (Person) applicationContext.getBean("personPrototype");

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

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

    ((AbstractApplicationContext) applicationContext).close();
}

The scopes.xml file is similar to the one presented in the previous section while adding the xml definition for the bean with the prototype scope:

scopes.xml文件与上一节介绍的文件类似,同时为Bean添加prototype作用域的xml定义。

<bean id="personPrototype" class="org.baeldung.scopes.Person" scope="prototype"/>

4. Web Aware Scopes

4.网络感知范围

As previously mentioned, there are four additional scopes that are only available in a web-aware application context. We use these less often in practice.

如前所述,还有四个额外的作用域,只有在网络感知的应用程序上下文中可用。我们在实践中较少使用这些。

The request scope creates a bean instance for a single HTTP request, while the session scope creates a bean instance for an HTTP Session.

request作用域为单个 HTTP 请求创建一个 bean 实例,而 session 作用域为一个 HTTP 会话创建一个 bean 实例。

The application scope creates the bean instance for the lifecycle of a ServletContext, and the websocket scope creates it for a particular WebSocket session.

应用范围为ServletContext的生命周期创建Bean实例,而websocket范围则为特定的WebSocketsession创建它。

Let’s create a class to use for instantiating the beans:

让我们创建一个用于实例化bean的类。

public class HelloMessageGenerator {
    private String message;
    
    // standard getter and setter
}

4.1. Request Scope

4.1.请求范围

We can define the bean with the request scope using the @Scope annotation:

我们可以使用@Scope注解在request范围内定义bean。

@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public HelloMessageGenerator requestScopedBean() {
    return new HelloMessageGenerator();
}

The proxyMode attribute is necessary because at the moment of the instantiation of the web application context, there is no active request. Spring creates a proxy to be injected as a dependency, and instantiates the target bean when it is needed in a request.

proxyMode属性是必要的,因为在Web应用上下文实例化的时刻,并没有活动的请求。Spring创建了一个代理,作为依赖关系注入,并在请求中需要目标Bean时将其实例化。

We can also use a @RequestScope composed annotation that acts as a shortcut for the above definition:

我们还可以使用一个@RequestScope组成的注解,作为上述定义的快捷方式。

@Bean
@RequestScope
public HelloMessageGenerator requestScopedBean() {
    return new HelloMessageGenerator();
}

Next we can define a controller that has an injected reference to the requestScopedBean. We need to access the same request twice in order to test the web specific scopes.

接下来我们可以定义一个控制器,它有一个对requestScopedBean的注入引用。我们需要访问同一个请求两次,以便测试网络的特定作用域。

If we display the message each time the request is run, we can see that the value is reset to null, even though it is later changed in the method. This is because of a different bean instance being returned for each request.

如果我们在每次运行请求时显示message,我们可以看到该值被重置为null,即使它后来在方法中被改变。这是因为每次请求都会返回一个不同的bean实例。

@Controller
public class ScopesController {
    @Resource(name = "requestScopedBean")
    HelloMessageGenerator requestScopedBean;

    @RequestMapping("/scopes/request")
    public String getRequestScopeMessage(final Model model) {
        model.addAttribute("previousMessage", requestScopedBean.getMessage());
        requestScopedBean.setMessage("Good morning!");
        model.addAttribute("currentMessage", requestScopedBean.getMessage());
        return "scopesExample";
    }
}

4.2. Session Scope

4.2.会议范围

We can define the bean with the session scope in a similar manner:

我们可以用类似的方式用sessionscope来定义Bean。

@Bean
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public HelloMessageGenerator sessionScopedBean() {
    return new HelloMessageGenerator();
}

There’s also a dedicated composed annotation we can use to simplify the bean definition:

还有一个专门的组成注释,我们可以用它来简化Bean的定义。

@Bean
@SessionScope
public HelloMessageGenerator sessionScopedBean() {
    return new HelloMessageGenerator();
}

Next we define a controller with a reference to the sessionScopedBean. Again, we need to run two requests in order to show that the value of the message field is the same for the session.

接下来我们定义一个控制器,引用sessionScopedBean。同样,我们需要运行两个请求,以显示message字段的值对会话是相同的。

In this case, when the request is made for the first time, the value message is null. However, once it is changed, that value is retained for subsequent requests as the same instance of the bean is returned for the entire session.

在这种情况下,当第一次提出请求时,message的值是null.。然而,一旦它被改变,这个值就会在随后的请求中被保留,因为整个会话都会返回同一个bean实例。

@Controller
public class ScopesController {
    @Resource(name = "sessionScopedBean")
    HelloMessageGenerator sessionScopedBean;

    @RequestMapping("/scopes/session")
    public String getSessionScopeMessage(final Model model) {
        model.addAttribute("previousMessage", sessionScopedBean.getMessage());
        sessionScopedBean.setMessage("Good afternoon!");
        model.addAttribute("currentMessage", sessionScopedBean.getMessage());
        return "scopesExample";
    }
}

4.3. Application Scope

4.3.应用范围

The application scope creates the bean instance for the lifecycle of a ServletContext.

应用范围为ServletContext.的生命周期创建Bean实例。

This is similar to the singleton scope, but there is a very important difference with regards to the scope of the bean.

这与singleton作用域相似,但在bean的作用域方面有一个非常重要的区别。

When beans are application scoped, the same instance of the bean is shared across multiple servlet-based applications running in the same ServletContext, while singleton scoped beans are scoped to a single application context only.

当Bean是应用作用域时,Bean的同一个实例被运行在同一个ServletContext中的多个基于Servlet的应用程序所共享,而singleton作用域的Bean只作用于一个应用程序上下文。

Let’s create the bean with the application scope:

让我们用application范围来创建bean。

@Bean
@Scope(
  value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public HelloMessageGenerator applicationScopedBean() {
    return new HelloMessageGenerator();
}

Analogous to the request and session scopes, we can use a shorter version:

类似于requestsession作用域,我们可以使用一个更短的版本。

@Bean
@ApplicationScope
public HelloMessageGenerator applicationScopedBean() {
    return new HelloMessageGenerator();
}

Now let’s create a controller that references this bean:

现在,让我们创建一个控制器,引用这个bean。

@Controller
public class ScopesController {
    @Resource(name = "applicationScopedBean")
    HelloMessageGenerator applicationScopedBean;

    @RequestMapping("/scopes/application")
    public String getApplicationScopeMessage(final Model model) {
        model.addAttribute("previousMessage", applicationScopedBean.getMessage());
        applicationScopedBean.setMessage("Good afternoon!");
        model.addAttribute("currentMessage", applicationScopedBean.getMessage());
        return "scopesExample";
    }
}

In this case, once set in the applicationScopedBean, the value message will be retained for all subsequent requests, sessions and even for different servlet applications that will access this bean, provided it is running in the same ServletContext.

在这种情况下,一旦在applicationScopedBean中设置,message的值将被保留给所有后续的请求、会话,甚至是访问该Bean的不同servlet应用程序,只要它在同一个ServletContext中运行。

4.4. WebSocket Scope

4.4.WebSocket范围

Finally, let’s create the bean with the websocket scope:

最后,让我们用websocket范围来创建Bean。

@Bean
@Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)
public HelloMessageGenerator websocketScopedBean() {
    return new HelloMessageGenerator();
}

When first accessed, WebSocket scoped beans are stored in the WebSocket session attributes. The same instance of the bean is then returned whenever that bean is accessed during the entire WebSocket session.

第一次访问时,WebSocket范围内的Bean被存储在WebSocket会话属性中。然后,在整个WebSocket会话期间,只要访问该Bean,就会返回该Bean的同一实例。

We can also say that it exhibits singleton behavior, but limited to a WebSocket session only.

我们也可以说,它表现出了单子行为,但只限于一个WebSocket会话。

5. Conclusion

5.结论

In this article, we discussed the different bean scopes provided by Spring and what their intended uses are.

在这篇文章中,我们讨论了Spring提供的不同的Bean作用域以及它们的预期用途。

The implementation of this article can be found in the GitHub project.

本文的实现可以在GitHub项目中找到。