Spring Bean作用域

72

有人能解释一下Spring beans中的作用域吗?我一直只用过“prototype”,但是我可以用其他参数代替它吗?

这是我说的一个示例:

<bean id="customerInfoController" class="com.action.Controller" scope="prototype">
    <property name="accountDao" ref="accountDao"/>
    <property name="utilityDao" ref="utilityDao"/>
    <property name="account_usageDao" ref="account_usageDao"/>  
</bean>

1
请查看Bean作用域 - Reimeus
http://www.mkyong.com/spring/spring-bean-scopes-examples/ - sanbhat
这里有一个更好的官方文档:http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/beans.html#beans-factory-scopes - KNU
3
非常好的解释在这里:http://codeflex.co/java-spring-scopes-explained/ (原文已经是英文,我将直接提供中文链接) - ybonda
我想添加一个更新:根据新的Spring发布,docs.spring现在有6个范围:Singleton、Prototype、Request、Session、Application、WebSocket。推荐官方Spring文档链接...而且...如果您不熟悉官方Spring文档的链接,这里有另一个简单的解释 - Aniket
9个回答

102
spring specs 可以了解到,Spring 支持五种 bean 作用域:

1. singleton(默认)

将一个 bean 定义范围限定为 Spring IoC 容器中的单个对象实例。

2. prototype

将一个 bean 定义范围限定为任意数量的对象实例。

3. request

将一个 bean 定义范围限定为单个 HTTP 请求的生命周期;也就是说,每个 HTTP 请求都会创建一个基于单个 bean 定义的实例。只在具有 Web 功能的 Spring ApplicationContext 上下文中有效。

4. session

将一个 bean 定义范围限定为 HTTP Session 的生命周期。只在具有 Web 功能的 Spring ApplicationContext 上下文中有效。

5. global session

将一个 bean 定义范围限定为全局 HTTP Session 的生命周期。通常仅在端口上下文中使用时才有效。只在具有 Web 功能的 Spring ApplicationContext 上下文中有效。

* 默认指没有在 <bean /> 标签中显式提供作用域时。 可在此处阅读有关它们的更多信息:http://static.springsource.org/spring/docs/3.0.0.M3/reference/html/ch04s04.html

1
“Web-aware Spring ApplicationContext”是一个扩展了ApplicationContext的WebApplicationContext。除了@JunedAhsan提到的额外范围之外,还有一个getServletContext()方法,用于“返回此应用程序的标准Servlet API ServletContext”。 - jumping_monkey

36
在Spring中,bean作用域用于决定从Spring容器返回哪种类型的bean实例给调用者。
支持5种bean作用域:
  1. Singleton:每个Spring IoC容器返回一个单个bean实例。此单个实例存储在这样的单例bean缓存中,并且对于该命名bean的所有后续请求和引用都会返回缓存对象。如果bean配置文件中未指定bean范围,则默认为单例。 enter image description here

  2. Prototype:每次请求时返回一个新的bean实例。它不像singleton一样存储任何缓存版本。 enter image description here

  3. Request:每个HTTP请求返回一个单个bean实例。

    enter image description here

  4. Session:每个HTTP会话(用户级别会话)返回一个单个bean实例。

  5. GlobalSession:每个全局HTTP会话返回一个单个bean实例。它仅在Web-aware Spring ApplicationContext上下文中有效(应用程序级别会话)。

在大多数情况下,您可能只需要处理Spring的核心范围——singletonprototype,默认范围是singleton

12

想向大家更新一下,在Spring 5中,如Spring文档中提到的那样,Spring支持6个范围(scope),其中四个仅在使用Web-aware的ApplicationContext时可用。

singleton(默认)将单个bean定义作用于每个Spring IoC容器中的单个对象实例。

prototype 将单个bean定义范围扩展到任意数量的对象实例。

request 将单个bean定义范围限定为单个HTTP请求的生命周期;即每个HTTP请求都有其自己的bean实例,基于一个单独的bean定义。仅在Web-aware的Spring ApplicationContext上下文中有效。

session 将单个bean定义范围限定为HTTP Session的生命周期。仅在Web-aware的Spring ApplicationContext上下文中有效。

application 将单个bean定义范围限定为ServletContext的生命周期。仅在Web-aware的Spring ApplicationContext上下文中有效。

websocket 将单个bean定义范围限定为WebSocket的生命周期。仅在Web-aware的Spring ApplicationContext上下文中有效。


总共有6个范围,其中前两个可以在没有Web感知的情况下使用吗?如果是这样,那么如何说明其中6个中有5个仅在使用Web感知的ApplicationContext时才可用? - MrKumar
你是对的。他犯了一个错误,他说成了5而不是4。 - Mahir Zukic

7

Spring文档描述了以下标准作用域:

singleton:(默认)将单个bean定义范围限定为Spring IoC容器中的单个对象实例。

prototype:将单个bean定义范围限定为任意数量的对象实例。

request:将单个bean定义范围限定为单个HTTP请求的生命周期;即每个HTTP请求都有自己的一个bean实例,该实例是从单个bean定义创建的。仅在Web-aware Spring ApplicationContext上下文中有效。

session:将单个bean定义的范围限定为HTTP会话的生命周期。仅在Web-aware Spring ApplicationContext上下文中有效。

global session:将单个bean定义的范围限定为全局HTTP会话的生命周期。通常仅在portlet上下文中使用时才有效。仅在Web-aware Spring ApplicationContext上下文中有效。

还可以使用CustomScopeConfigurer创建和配置其他自定义范围。一个例子是由Spring Webflow添加的flow范围。

顺便说一下,你声称你总是使用prototype,我觉得很奇怪。标准范围是singleton,在我开发的应用程序中,我很少需要原型作用域。你可能应该看一下这个。


6

可以在Spring bean scopes中找到每个范围的详细说明。以下是摘要:

Singleton - (默认)使单个bean定义适用于Spring IoC容器中的单个对象实例。

prototype - 使单个bean定义适用于任意数量的对象实例。

request - 使单个bean定义适用于单个HTTP请求的生命周期;也就是说,每个HTTP请求都有自己的bean实例,这些实例是从一个单独的bean定义创建的。仅在Web-aware Spring ApplicationContext的上下文中有效。

session - 使单个bean定义适用于HTTP Session的生命周期。仅在Web-aware Spring ApplicationContext的上下文中有效。

global session - 使单个bean定义适用于全局HTTP Session的生命周期。通常仅在portlet上下文中使用时有效。仅在Web-aware Spring ApplicationContext的上下文中有效。


3

一个简单的例子,展示了@Scope("singleton")(默认)和@Scope("prototype")之间的区别:

DAO类:

package com.example.demo;

public class Manager {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

配置:

@Configuration
public class AppConfiguration {
    @Bean
    @Scope("singleton")
    public Manager getManager(){
        return new Manager();
    }
}

和MainApp:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.scan("com.example.demo");
        context.refresh();

        Manager firstManager = context.getBean(Manager.class);
        firstManager.setName("Karol");

        Manager secondManager = context.getBean(Manager.class);
        System.out.println(secondManager.getName());
    }

}

在这个例子中的结果是:Karol,即使我们只为firstManager对象设置了这个名字。这是因为Spring IoC容器创建了一个对象实例。然而,当我们在配置类中将作用域改为@Scope("prototype")时,结果是:null,因为当请求该bean时,Spring IoC容器会创建一个新的bean实例。

2
根据 Spring-Cloud-Config 的 文档,除了现有的五个范围之外,还有一个额外的范围@RefreshScope
这是RefreshScope的简短描述:
当配置更改时,标记为@RefreshScope的Spring @Bean会得到特殊处理。此功能解决了仅在初始化时才获取其配置的有状态bean的问题。例如,如果DataSource在通过环境更改数据库URL时有打开的连接,则可能希望拥有那些连接的持有者能够完成其工作。然后,下次有东西从池中借用连接时,它就会获得具有新URL的连接。
有时,甚至必须将@RefreshScope注释应用于仅可初始化一次的某些bean。如果bean是“不可变的”,则必须使用@RefreshScope注释该bean或在属性键spring.cloud.refresh.extra-refreshable下指定类名。
刷新作用域bean是惰性代理,当它们被使用(即调用方法时)时初始化,并且该范围充当已初始化值的缓存。要强制bean在下一个方法调用时重新初始化,必须使其缓存条目无效。
RefreshScope是上下文中的一个bean,并具有公共refreshAll()方法,以通过清除目标缓存来刷新范围内的所有bean。 /refresh端点公开此功能(通过HTTP或JMX)。要按名称刷新单个bean,还有一个refresh(String)方法。

0

同时还添加了websocket范围:

将单个bean定义的作用域限定为WebSocket的生命周期。仅在具有Web感知Spring ApplicationContext的上下文中有效。

根据文档内容,还有线程作用域,但默认情况下未注册。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接