Spring Bean的作用域

article/2025/9/22 22:15:39

在Spring中,bean作用域用于确定哪种类型的bean实例应该从Spring容器中返回给调用者。

目前Spring Bean的作用域或者说范围主要有五种。

作用域描述
singleton在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,bean作用域范围的默认值。
prototype每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()。
request每次HTTP请求都会创建一个新的Bean,该作用域仅适用于web的Spring WebApplicationContext环境。
session同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。该作用域仅适用于web的Spring WebApplicationContext环境。
application限定一个Bean的作用域为ServletContext的生命周期。该作用域仅适用于web的Spring WebApplicationContext环境。

(1)被声明为singleton的bean

如果bean的作用域的属性被声明为singleton,那么Spring Ioc容器只会创建一个共享的bean实例。对于所有的bean请求,只要id与该bean定义的相匹配,那么Spring在每次需要时都返回同一个bean实例。

Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,singleton作用域是Spring中的缺省作用域。你可以在 bean 的配置文件中设置作用域的属性为 singleton,如下所示:

<!-- A bean definition with singleton scope -->
<bean id="..." class="..." scope="singleton"><!-- collaborators and configuration for this bean go here -->
</bean>

单例的例子

1.首先创建一个bean。

package com.spring.demo;
public class  SingletonBean{private String message;public void setMessage(String message){this.message  = message;}public void getMessage(){System.out.println("Your Message : " + message);}
}

2.在Spring的配置文件中配置该bean。

<?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="SingletonBean" class="com.spring.demo.SingletonBean" scope="singleton"></bean><!-- 或者 --><!--  <bean id="SingletonBean" class="com.spring.demo.SingletonBean" ></bean> -->
</beans>

测试该Bean是否为单例的。

package com.spring.demo;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.junit.Test;public class TestBean {@Testpublic void textUser(){//1.获取spring文件ApplicationContext context = new ClassPathXmlApplicationContext("Bean.xml");//2.由配置文件返回对象SingletonBean singletonBeanA = (SingletonBean)context.getBean("SingletonBean");singletonBeanA.setMessage("I'm object A");singletonBeanA.getMessage();SingletonBean singletonBeanB = (SingletonBean)context.getBean("SingletonBean");singletonBeanB.getMessage();}
}

运行结果:

由于SingletonBean是单例的作用域,创建两个SingletonBean对象,第二个对象获取SingletonBean对象中的消息值得时候即使是由一个新的getBean()方法来获取,也可以不用设置对象中消息的值就可以直接获取SingletonBean中的消息,因为这时的消息已经由第一个对象初始化了。在单例中,每个Spring IoC容器只有一个实例,无论创建多少个对象,调用多少次getMessafe( )方法获取它,它总是返回同一个实例。 

(2)被声明为prototype的bean

当一个bean的作用域为prototype,表示一个bean定义对应多个对象实例。声明为prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。

prototype的例子。

还是上面的代码。其他代码不变,把Bean.xml文件中bean的作用域由singleton改为prototype。

<?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="SingletonBean" class="com.spring.demo.SingletonBean" scope="prototype"></bean></beans>

执行代码,程序的执行结果为:

从图上可以看出在SingletonBeanA中设置的参数值在SingletonBeanB就获取不到了,说明这两个对象现在返回的就不是同一个对象实例。

(3)使用注解定义 bean 的作用域。

除了在Bean.xml文件中定义bean的作用域之外,还可以使用注解来定义 bean 的作用域。

1.在Bean中加上注解。

package com.spring.demo;import org.springframework.context.annotation.Scope;;
import org.springframework.stereotype.Component;@Component("SingletonBean")
@Scope("prototype")
public class SingletonBean {private String message;public void setMessage(String message){this.message  = message;}public void getMessage(){System.out.println("Your Message : " + message);}
}

@Component("SingletonBean")注解是告诉Spring这是一个bean。
@Scope("prototype") 注解是告诉Spring该bean的作用域是prototype。

2.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"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.2.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.2.xsd"><context:component-scan base-package="com.spring.demo" /></beans>

<context:component-scan base-package="com.spring.demo" />就是扫描com.spring.demo包中的所有类的注解。

测试代码不用变,运行测试。

和在bean.xml中直接定义bean和其作用域是一样的效果。其他作用域也可以使用注解方式声明bean的作用域。

request,session和application这三个作用域都是基于web的Spring WebApplicationContext实现的,只有在web环境下(比如XmlWebApplicationContext)中才能使用。 
如果开发者仅仅在常规的Spring IoC容器中比如ClassPathXmlApplicationContext在中使用这些作用域,那么将会抛出一个IllegalStateException来说明使用了未知的作用域。

也就是当用户使用Spring的WebApplicationContext时,除了使用常规的singleton和prototype作用域之外,还可以使用另外3种Bean的作用域,即request,session和application。

在使用Web应用环境相关的Bean作用域时,必须在Web容器中进行一些额外的配置:

1.如果开发者使用了Spring Web MVC框架的话,每一个请求都会通过Spring的DispatcherServlet来处理,也就没有其他特殊的初始化配置,就不需要配置了。DispatcherServlet已经包含了相关的状态。

2.如果开发者使用的是低版本Web容器比如Servlet 2.5的web容器,请求不是通过Spring的DispatcherServlet(比如JSF或者Struts)来处理的。那么开发者需要注册org.springframework.web.context.request.RequestContextListener或者ServletRequestListener。可以在web.xml中增加如下的Listener声明: 

<web-app>...<listener><listener-class>org.springframework.web.context.request.RequestContextListener</listener-class></listener>...
</web-app>

ServletContextListener只负责监听web容器启动和关闭的事件,而RequestContextListener实现了ServletRequestListener监听器接口,该监听器监听http请求事件。Web服务器接收到的每一次请求都会通知该监听器。 

而在Servlet 3.0以后,这些都能够通过WebApplicationInitializer接口来实现配置。

3.如果不使用Listener,也可以考虑使用Spring的RequestContextFilter,通过http过滤器进行配置,在url-pattern中对所有的页面进行过滤。也是在web.xml中进行配置。

<web-app>...<filter><filter-name>requestContextFilter</filter-name><filter-class>org.springframework.web.filter.RequestContextFilter</filter-class></filter><filter-mapping><filter-name>requestContextFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>...
</web-app>

配置完这些额外的配置之后,就可以使用另外的3种bean的作用域了。 

(4)请求作用域

请求作用域参考如下的Bean定义

<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>

Spring容器会在每次用到loginAction来处理每个HTTP请求的时候都会创建一个新的LoginAction实例。也就是说,loginActionBean的作用域是HTTP Request级别的。 
当http请求调用作用域为request的bean的时候,每增加一个HTTP请求,Spring就会创建一个新的bean,在请求处理完成之后便及时销毁这个bean。开发者可以随意改变实例的状态,因为通过loginAction请求来创建的其他实例根本看不到开发者改变的实例状态,所有创建的Bean实例都是根据独立的请求来的。

(5)会话作用域

会话作用域参考如下的Bean定义

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

Spring容器会在每次调用到userPreferences时,在一个单独的HTTP会话周期来创建一个新的UserPreferences实例。换言之,userPreferencesBean的作用域是HTTP Session级别的。

Session中所有http请求共享同一个请求的bean实例。Session结束后就销毁bean。 在request-scoped作用域的Bean上,开发者可以随意的更改实例的状态。同样,使用从同一个userPreferences bean定义创建的其他HTTP Session实例在看不到不是自己的内部状态的修改,因为他们是单个的HTTP会话。每个Session请求都会创建新的userPreferences实例,所以开发者更改一个Bean的状态,对于其他的Bean仍然是不可见的。

(6)全局作用域

全局作用域参考如下的Bean定义

<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>

Spring容器会在整个web应用范围使用到appPreferences的时候创建一个新的AppPreferences的实例。也就是说,appPreferencesBean是在ServletContext级别的,作为常规的ServletContext属性。这种作用域在一些程度上来说和Spring的单例作用域相似,但是也有如下不同之处:

1.application作用域是每个ServletContext中包含一个,而不是每个SpringApplicationContext之中包含一个(某些应用中可能包含不止一个ApplicationContext)。

2.application作用域仅仅作为ServletContext的属性可见,单例Bean是ApplicationContext可见。

接下来再来简单的学习下在Spring当中如何自定义作用域:

在Spring 2.0中,Spring的Bean作用域机制是可以扩展的,这意味着,你不仅可以使用Spring提供的预定义Bean作用域,还可以定义自己的作用域,甚至重新定义现有的作用域(不提倡这么做,而且你不能覆盖内置的singleton和prototype作用域)

(7)自定义作用域

除了使用Spring已经定义好的作用域之外,还可以自定义bean的作用域。

要底线自定义作用域

1.首先需要实现自定义Scope类。

首先要先实现org.springframework.beans.factory.config.Scope这个接口,要将自定义scope集成到Spring容器当中就必须要实现这个接口。接口中有两个常用的方法,分别用于底层存储机制获取和删除这个对象。

2.在实现一个或多个自定义Scope并测试通过之后,接下来便是如何让Spring容器来识别新的作用域。registerScope方法就是在Spring容器中用来注册新的作用域。

void registerScope(String scopeName, Scope scope);

其中:第一个参数是与作用域相关的全局唯一的名称,第二个参数是准备实现的作用域的实例,就是实现Scope接口的实例。

比如实现Scope接口的类为SimpleThreadScope,要实现的自定义的bean的作用域的名称为“thread”,那就可以这么写。

Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);

3.在实现和注册自定义的scope类之后,就可以通过如下类似的Bean定义来使用自定义的Scope:

<bean id="..." class="..." scope="thread">

另外,在自定义的Scope中,开发者也不限于仅仅通过编程方式来实现自定义的bean的作用域,也可以在Spring的配置文件中配置和使用自定义作用域和,比如配置CustomScopeConfigurer实例实现自定义的作用域,声明作用域名称为“thread”,就可以在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"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"><property name="scopes"><map><entry key="thread"><bean class="org.springframework.context.support.SimpleThreadScope"/></entry></map></property></bean><bean id="bar" class="x.y.Bar" scope="thread"><property name="name" value="Rick"/><aop:scoped-proxy/></bean><bean id="foo" class="x.y.Foo"><property name="bar" ref="bar"/></bean></beans>

以上就是Spring Bean作用域的一些基本信息。


http://chatgpt.dhexx.cn/article/C82iFWES.shtml

相关文章

设计原则-依赖倒置

名称&#xff1a;依赖倒置原则&#xff08;Dependence Inversion Principle&#xff09; 定义&#xff1a;程序要依赖于抽象接口&#xff0c;不要依赖于具体实现。依赖倒置原则的中心思想是面向接口编程。 高层模块不应依赖于低层模块&#xff0c;二者都应该依赖于抽象抽象不…

C++之依赖倒置原则

C之依赖倒置原则 1. 依赖于具体抽象&#xff08;接口&#xff09;&#xff0c;不依赖于具体的实现&#xff0c;也就是针对接口编程。 2. 实现高层业务和实现层、实现层和实现层之间的解耦合&#xff1b; 实例&#xff1a; 电脑框架和电脑内部的配件产品&#xff08;或提供配…

【软件架构设计原则】开闭原则和依赖倒置原则

文章目录 软件架构设计原则开闭原则依赖倒置原则最后 软件架构设计原则 本文通过实例来讲解 开闭原则依赖导致原则 开闭原则 开闭原则&#xff08;Open-Close Principle&#xff0c;OCP&#xff09;是指一个软件实体&#xff08;如类、模块和函数&#xff09;应该对扩展开放…

java 依赖倒置_Java设计原则—依赖倒置原则(转)

依赖倒置原则(Dependence Inversion Principle&#xff0c;DIP)的原始定义&#xff1a; 高层模块不应该依赖底层模块&#xff0c;两者都应该依赖其抽象&#xff1b; 抽象不应该依赖细节&#xff1b; 细节应该依赖抽象。 依赖倒置原则在Java语言中的表现是&#xff1a; 模块间的…

设计模式 依赖倒置原则

文章目录 依赖倒置原则依赖倒置原则实战使用依赖倒置原则进行改变案例中两种方式的类图 依赖倒置原则 定义: 程序要依赖于抽象接口, 不要依赖于具体实现, 对抽象进行编程, 而不是对实现进行编程, 降低了客户与实现模块的耦合. 高层模块不应该依赖底层模块, 都应该依赖抽象(接口…

依赖倒置原则应用-司机开车案例

依赖倒置原则 依赖倒置原则(Dependence Inversion Principle)是程序要依赖于抽象接口&#xff0c;不要依赖于具体实现。简单的说就是要求对抽象进行编程&#xff0c;不要对实现进行编程&#xff0c;这样就降低了客户与实现模块间的耦合。 依赖倒置原则三层含义&#xff1a; …

4.3 依赖倒置原则

一. 依赖倒置原则的定义 1.核心思想 依赖倒置原则&#xff08;Dependence Inversion Principle&#xff0c;DIP&#xff09; *1.高层模块 不应该依赖 低层模块, 二者都应该依赖其抽象; *2.抽象 不应该依赖 细节, 细节 应该依赖 抽象; 接口和抽象类价值在于设计; *3.依赖倒…

C# 依赖倒置原则(DIP)

目录 一&#xff0c;引子 1.1 传统的程序架构 1.2 依赖倒置 1.3 依赖倒置的作用 二&#xff0c;依赖注入 一&#xff0c;引子 1.1 传统的程序架构 在程序执行过程中&#xff0c;传统的程序架构如图&#xff1a; 可以看到&#xff0c;在传统的三层架构中&#xff0c;层与…

设计模式-依赖倒置原则

依赖倒置原则 1 依赖倒置原则的定义 依赖倒置原则&#xff08;Dependence Inversion Principle,DIP&#xff09;这个名字看着有点别扭&#xff0c;“依赖”还“倒置”&#xff0c;这到底是什么意思&#xff1f;依赖倒置原则的原始定义是&#xff1a; High level modules shou…

C++ 依赖倒置原则

.依赖倒置原则 定义 高层模块不应该依赖于底层模块&#xff0c;而应该依赖于抽象。抽象不应依赖于细节&#xff0c;细节应依赖于抽象。解决的问题 类A直接依赖类B&#xff0c;假如要将类A改为依赖类C&#xff0c;则必须通过修改类A的代码来达成。这种场景下&#xff0c;类A一般…

【设计原则】依赖倒置原则 (面向接口编程)

本文将以Java为基础&#xff0c;讲解开发中&#xff0c;面向接口编程的知识&#xff0c;只要以简单的例子为主&#xff0c;讲解如何进行面向接口编程,并会区分其于面向实现编程的区别。下面先讲一讲依赖倒置原则&#xff0c;再过渡到案例解释。 本文目的在于用极其简单的图解帮…

设计模式 — 6大设计原则(依赖倒置和接口隔离原则)

设计模式 依赖倒置原则示例 一示例 二依赖的三种写法总结 接口隔离原则实例 一总结 依赖倒置原则 依赖倒置原则&#xff08;Dependence Inversion Principle&#xff0c;DIP&#xff09;这个名字看着有点别扭&#xff0c;“依赖” 还 “倒置” &#xff0c;这到底是什么意思&a…

【六大设计原则-SOLID】

SOLID简介&#xff1a; 历史&#xff1a;由Robert CMartin汇总并推广 目标&#xff1a; 使软件更容易被改动是软件更容易被理解构建可以在多个软件系统中复用的组件 组成&#xff1a; 名称简写含义单一职责原则 SRP Single Responsibility Principle 初始定义&#xff08…

SOLID原则:解释和实例

在面向对象编程中,SOLID是5个重要的设计原则的缩写。首先是由著名的软件大师Robert C.Martin (Bob 大叔)在Design Principles and Design Patterns 中提出, 后来Michael Feathers 用SOLID来概括这五大原则。 SOLID原则使得软件设计更加容易理解、灵活和可维护。作为一名软件…

SOLID原则的含义和具体使用

单一职责原则&#xff08;SRP&#xff09;开放封闭原则&#xff08;OCP&#xff09;里氏替换原则&#xff08;LSP&#xff09;接口隔离原则&#xff08;ISP&#xff09;依赖倒置原则&#xff08;DIP&#xff09;小结 SOLID 是面向对象设计5大重要原则的首字母缩写&#xff0c;当…

设计模式:SOLID原则

单一职责原则 Single Responsibility Principle&#xff08;SRP&#xff09; 接口职责应该单一&#xff0c;不要承担过多的职责。 开放封闭原则 Open Closed Principle&#xff08;OCP&#xff09; 添加一个新的功能应该是&#xff0c;在已有代码基础上扩展代码&#xff08;…

设计模式之SOLID原则

介绍 设计模式中的SOLID原则&#xff0c;分别是单一原则、开闭原则、里氏替换原则、接口隔离原则、依赖倒置原则。前辈们总结出来的&#xff0c;遵循五大原则可以使程序解决紧耦合&#xff0c;更加健壮。 SRP 单一责任原则 OCP …

SOLID 原则要这么理解!

什么是 SOLID 原则 SOLID 原则其实是用来指导软件设计的&#xff0c;它一共分为五条设计原则&#xff0c;分别是&#xff1a; 单一职责原则&#xff08;SRP&#xff09;开闭原则&#xff08;OCP&#xff09;里氏替换原则&#xff08;LSP&#xff09;接口隔离原则&#xff08;…

SOLID 设计原则 (有点长但很透彻)

面向对象设计原则 SOLID 应该是职业程序员必须掌握的基本原则&#xff0c;每个程序员都应该了然于胸&#xff0c;遵守这 5个原则可以帮助我们写出易维护、易拓展的高内聚低耦合的代码。 它是由罗伯特C马丁(知名的 Rob 大叔)21世纪初期 (准确来说&#xff0c;2000年在他的论文De…

软件开发SOLID设计原则

前言&#xff1a;SOLID设计原则&#xff0c;不管是软件系统还是代码的实现&#xff0c;遵循SOLID设计原则&#xff0c;都能够有效的提高系统的灵活和可靠性&#xff0c;应对代码实现的需求变化也能更好的扩展和维护。因此提出了五大原则——SOLID。 我是通过老师讲解以及老师…