Spring Bean作用域
默认情况下,所有的 Spring Bean 都是单例的,也就是说在整个 Spring 应用中, Bean 的实例只有一个。
我们可以在 <bean> 元素中添加 scope 属性来配置 Spring Bean 的作用范围。例如,如果每次获取 Bean 时,都需要一个新的 Bean 实例,那么应该将 Bean 的 scope 属性定义为 prototype,如果 Spring 需要每次都返回一个相同的 Bean 实例,则应将 Bean 的 scope 属性定义为 singleton。
Spring 5 共提供了 6 种 scope 作用域,如下表。
如果一个 Bean 定义的作用域为 singleton ,那么这个 Bean 就被称为 singleton bean。在 Spring IoC 容器中,singleton bean 是 Bean 的默认创建方式,可以更好地重用对象,节省重复创建对象的开销。
在 Spring 配置文件中,可以使用 <bean> 元素的 scope 属性,将 Bean 的作用域定义成 singleton,其配置方式如下所示:
1. 参考《第一个 Spring 程序》,新建一个名为 my-spring-scope-demo 的 Java 项目。
2. 在 my-spring-scope-demo 中,新建一个名为 net.biancheng.c 的包,并在这个包下创建一个名为 SingletonBean 的类,代码如下。
3. 在 src 目录下创建 Spring 配置文件 Beans.xml,配置如下。
4. 在 net.biancheng.c 包下,创建一个名为 MainApp 的类,代码如下。
5. 运行 MainApp 类中的 main() 方法,控制台输出如下。
从控制台的输出可以看出,两次获得的 Bean 实例的地址完全一样,这说明 IoC 容器只创建了一个 singletonBean 实例。由于 singleton 是 Spring IoC 容器的默认作用域,因此即使省略 scope 属性,控制台的输出结果也一样的。
1. 在 net.biancheng.c 包中,创建一个名为 PrototypeBean 的类,代码如下。
2. 在 Beans.xml 中,添加 PrototypeBean 的 Bean 的定义,配置如下。
3. 对 MainApp 中的代码进行修改。
4. 执行 MainApp 中的 main 函数,控制台输出如下。
从运行结果可以看出,两次输出的内容并不相同,这说明在 prototype 作用域下,Spring 容器创建了两个不同的 prototypeBean 实例。
我们可以在 <bean> 元素中添加 scope 属性来配置 Spring Bean 的作用范围。例如,如果每次获取 Bean 时,都需要一个新的 Bean 实例,那么应该将 Bean 的 scope 属性定义为 prototype,如果 Spring 需要每次都返回一个相同的 Bean 实例,则应将 Bean 的 scope 属性定义为 singleton。
Spring 5 共提供了 6 种 scope 作用域,如下表。
作用范围 | 描述 |
---|---|
singleton | 默认值,单例模式,表示在 Spring 容器中只有一个 Bean 实例 |
prototype | 原型模式,表示每次通过 Spring 容器获取 Bean 时,容器都会创建一个新的 Bean 实例。 |
request | 每次 HTTP 请求,容器都会创建一个 Bean 实例。该作用域只在当前 HTTP Request 内有效。 |
session | 同一个 HTTP Session 共享一个 Bean 实例,不同的 Session 使用不同的 Bean 实例。该作用域仅在当前 HTTP Session 内有效。 |
application |
同一个 Web 应用共享一个 Bean 实例,该作用域在当前 ServletContext 内有效。 与 singleton 类似,但 singleton 表示每个 IoC 容器中仅有一个 Bean 实例,而一个 Web 应用中可能会存在多个 IoC 容器,但一个 Web 应用只会有一个 ServletContext,也可以说 application 才是 Web 应用中货真价实的单例模式。 |
websocket | websocket 的作用域是 WebSocket ,即在整个 WebSocket 中有效。 |
本节,我们就只对 singleton 和 prototype 这两种 Bean 作用域进行详细的讲解,至于其他 Bean 作用域,我们会在后续的教程中陆续进行介绍。注意:在以上 6 种 Bean 作用域中,除了 singleton 和 prototype 可以直接在常规的 Spring IoC 容器(例如 ClassPathXmlApplicationContext)中使用外,剩下的都只能在基于 Web 的 ApplicationContext 实现(例如 XmlWebApplicationContext)中才能使用,否则就会抛出一个 IllegalStateException 的异常。
singleton
singleton 是 Spring 容器默认的作用域。当 Bean 的作用域为 singleton 时,Spring IoC 容器中只会存在一个共享的 Bean 实例。这个 Bean 实例将存储在高速缓存中,所有对于这个 Bean 的请求和引用,只要 id 与这个 Bean 定义相匹配,都会返回这个缓存中的对象实例。如果一个 Bean 定义的作用域为 singleton ,那么这个 Bean 就被称为 singleton bean。在 Spring IoC 容器中,singleton bean 是 Bean 的默认创建方式,可以更好地重用对象,节省重复创建对象的开销。
在 Spring 配置文件中,可以使用 <bean> 元素的 scope 属性,将 Bean 的作用域定义成 singleton,其配置方式如下所示:
<bean id="..." class="..." scope="singleton"/>
示例 1
下面我们就通过一个简单的实例,对 Bean 的 singleton 作用域进行演示。1. 参考《第一个 Spring 程序》,新建一个名为 my-spring-scope-demo 的 Java 项目。
2. 在 my-spring-scope-demo 中,新建一个名为 net.biancheng.c 的包,并在这个包下创建一个名为 SingletonBean 的类,代码如下。
package net.biancheng.c; public class SingletonBean { private String str; public void setStr(String str) { this.str = str; } }
3. 在 src 目录下创建 Spring 配置文件 Beans.xml,配置如下。
<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-3.0.xsd"> <bean id="singletonBean" class="net.biancheng.c.SingletonBean" scope="singleton"> <property name="str" value="C语言中文网"></property> </bean> </beans>
4. 在 net.biancheng.c 包下,创建一个名为 MainApp 的类,代码如下。
package net.biancheng.c; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { private static final Log LOGGER = LogFactory.getLog(MainApp.class); public static void main(String[] args) { //获取 ApplicationContext 容器 ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); SingletonBean singletonBean = context.getBean("singletonBean", SingletonBean.class); SingletonBean singletonBean2 = context.getBean("singletonBean", SingletonBean.class); System.out.println(singletonBean); System.out.println(singletonBean2); } }
5. 运行 MainApp 类中的 main() 方法,控制台输出如下。
net.biancheng.c.SingletonBean@65e2dbf3 net.biancheng.c.SingletonBean@65e2dbf3
从控制台的输出可以看出,两次获得的 Bean 实例的地址完全一样,这说明 IoC 容器只创建了一个 singletonBean 实例。由于 singleton 是 Spring IoC 容器的默认作用域,因此即使省略 scope 属性,控制台的输出结果也一样的。
prototype
如果一个 Bean 定义的作用域为 prototype,那么这个 Bean 就被称为 prototype bean。对于 prototype bean 来说,Spring 容器会在每次请求该 Bean 时,都创建一个新的 Bean 实例。在 Spring 配置文件中,可以使用 <bean> 元素的 scope 属性将 Bean 的作用域定义成 prototype,其配置方式如下所示:从某种意义上说,Spring IoC 容器对于 prototype bean 的作用就相当于 Java 的 new 操作符。它只负责 Bean 的创建,至于后续的生命周期管理则都是由客户端代码完成的,详情请参看《Spring Bean 生命周期》。
<bean id="..." class="..." scope="prototype"/>
示例 2
下面我们就通过一个简单的实例,对 Bean 的 prototype 作用域进行演示。1. 在 net.biancheng.c 包中,创建一个名为 PrototypeBean 的类,代码如下。
package net.biancheng.c; public class PrototypeBean { private String str; public void setStr(String str) { this.str = str; } }
2. 在 Beans.xml 中,添加 PrototypeBean 的 Bean 的定义,配置如下。
<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-3.0.xsd"> <!--单例模式 singleton--> <bean id="singletonBean" class="net.biancheng.c.SingletonBean" scope="singleton"> <property name="str" value="C语言中文网"></property> </bean> <!--原型模式 prototype--> <bean id="prototypeBean" class="net.biancheng.c.PrototypeBean" scope="prototype"> <property name="str" value="c.biancheng.net"></property> </bean> </beans>
3. 对 MainApp 中的代码进行修改。
package net.biancheng.c; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { private static final Log LOGGER = LogFactory.getLog(MainApp.class); public static void main(String[] args) { //获取 ApplicationContext 容器 ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); PrototypeBean prototypeBean = context.getBean("prototypeBean", PrototypeBean.class); PrototypeBean prototypeBean2 = context.getBean("prototypeBean", PrototypeBean.class); System.out.println(prototypeBean); System.out.println(prototypeBean2); } }
4. 执行 MainApp 中的 main 函数,控制台输出如下。
net.biancheng.c.PrototypeBean@61f8bee4 net.biancheng.c.PrototypeBean@7b49cea0
从运行结果可以看出,两次输出的内容并不相同,这说明在 prototype 作用域下,Spring 容器创建了两个不同的 prototypeBean 实例。