SpringBean作用域
在配置文件中,除了可以定义Bean的属性值和相互之间的依赖关系,还可以声明Bean的作用域。例如,如果每次获取Bean时,都需要一个Bean实例,那么应该将Bean的scope属性定义为prototype,如果Spring需要每次都返回一个相同的Bean实例,则应将Bean的scope属性定义为singleton。
作用域的种类
Spring容器在初始化一个Bean实例时,同时会指定该实例的作用域。Spring5支持以下6种作用域。
1)singleton
默认值,单例模式,表示在Spring容器中只有一个Bean实例,Bean以单例的方式存在。
2)prototype
原型模式,表示每次通过Spring容器获取Bean时,容器都会创建一个Bean实例。
3)request
每次HTTP请求,容器都会创建一个Bean实例。该作用域只在当前HTTPRequest内有效。
4)session
同一个HTTPSession共享一个Bean实例,不同的Session使用不同的Bean实例。该作用域仅在当前HTTPSession内有效。
5)application
同一个Web应用共享一个Bean实例,该作用域在当前ServletContext内有效。
类似于singleton,不同的是,singleton表示每个IoC容器中仅有一个Bean实例,而同一个Web应用中可能会有多个IoC容器,但一个Web应用只会有一个ServletContext,也可以说application才是Web应用中货真价实的单例模式。
6)websocket
websocket的作用域是WebSocket,即在整个WebSocket中有效。
注意:Spring5版本之前还支持globalSession,该值表示在一个全局的HTTPSession中,容器会返回该Bean的同一个实例。一般用于Portlet应用环境。Spring5.2.0版本中已经将该值移除了。
request、session、application、websocket和globalSession作用域只能在Web环境下使用,如果使用ClassPathXmlApplicationContext加载这些作用域中的任意一个的Bean,就会抛出以下异常。
java.lang.IllegalStateException: No Scope registered for scope name 'xxx'1复制代码类型:[java]
下面我们详细讲解常用的两个作用域:singleton和prototype。
singleton
singleton是Spring容器默认的作用域。当Bean的作用域为singleton时,Spring容器中只会存在一个共享的Bean实例。该Bean实例将存储在高速缓存中,并且所有对Bean的请求,只要id与该Bean定义相匹配,都会返回该缓存对象。
通常情况下,这种单例模式对于无会话状态的Bean(如DAO层、Service层)来说,是最理想的选择。
在Spring配置文件中,可以使用<bean>元素的scope属性,将Bean的作用域定义成singleton,其配置方式如下所示:
<bean id="..." class="..." scope="singleton"/>1复制代码类型:[java]
例1
下面使用EclipseIDE演示如何将Bean的作用域指定为singleton,步骤如下:
1、创建SpringDemo项目,并在src目录下创建net.biancheng包。
2、添加相应的jar包,可以参考《第一个Spring程序》一节。
3、在net.biancheng包下创建HelloWorld和MainApp类。
4、在src目录下创建Spring配置文件Beans.xml。
运行SpringDemo项目。
HelloWorld类代码如下。
package net.biancheng;public class HelloWorld { private String message; public void setMessage(String message) { this.message = message; } public void getMessage() { System.out.println("message : " + message); } }12345678910复制代码类型:[java]
MainApp类如下。
package net.biancheng;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class MainApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); HelloWorld objA = (HelloWorld) context.getBean("helloWorld"); objA.setMessage("对象A"); objA.getMessage(); HelloWorld objB = (HelloWorld) context.getBean("helloWorld"); objB.getMessage(); } }12345678910111213复制代码类型:[java]
Beans.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-3.0.xsd"> <bean id="helloWorld" class="net.biancheng.HelloWorld" scope="singleton"/> </beans>12345678复制代码类型:[java]
运行结果如下。
message : 对象A message : 对象A12复制代码类型:[java]
从运行结果可以看出,两次输出内容相同,这说明Spring容器只创建了一个HelloWorld类的实例。由于Spring容器默认作用域是singleton,所以如果省略scope属性,其输出结果也会是一个实例。
prototype
对于prototype作用域的Bean,Spring容器会在每次请求该Bean时都创建一个新的Bean实例。prototype作用域适用于需要保持会话状态的Bean(如Struts2的Action类)。
在Spring配置文件中,可以使用<bean>元素的scope属性,将Bean的作用域定义成prototype,其配置方式如下所示:
<bean id="..." class="..." scope="prototype"/>1复制代码类型:[java]
例2
在例1的基础上,修改配置文件Beans.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-3.0.xsd"> <bean id="helloWorld" class="net.biancheng.HelloWorld" scope="prototype"/> </beans>12345678复制代码类型:[java]
运行结果如下。
message : 对象A message : null12复制代码类型:[java]
从运行结果可以看出,两次输出的内容并不相同,这说明在prototype作用域下,Spring容器创建了两个不同的HelloWorld实例。