前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring中bean的作用域scope详解

Spring中bean的作用域scope详解

作者头像
可为编程
发布2023-11-14 11:39:19
1780
发布2023-11-14 11:39:19
举报

戳上方蓝字“可为编程” 点击右上角选择“设为星标”,好文不错过!

在Spring应用中,有时候我们需要一个对象在整个应用中只有一个,有些对象希望每次使用的时候都重新创建一个,Spring对我们这种需求也提供了支持,在Spring中这个叫做bean的作用域,xml中定义bean的时候,可以通过scope属性指定bean的作用域,如:

  1. <bean id="" class="" scope="作用域" />

spring容器中scope常见的有5种,下面我们分别来介绍一下,我们还是先通过Xml配置文件然后再通过注解两种形式来进行案例模拟,开干。

singleton

当scope的值设置为singleton的时候,整个spring容器中只会存在一个bean实例,IOC容器多次查找bean的时候(调用BeanFactory的getBean方法或者bean之间注入依赖的bean对象的时候),返回的都是同一个bean对象,singleton是scope的默认值,所以spring容器中默认创建的bean对象是单例的,通常spring容器在启动的时候,会默认将Bean设置为scope为singleton的单例作用域,在容器中创建好供使用者调用。但是有个特殊的情况,当bean的lazy被设置为true的时候,表示懒加载,那么只有在使用的时候才会创建,用的时候直接返回。

bean xml配置

代码语言:javascript
复制
<!-- 单例bean,scope设置为singleton --><bean id="singletonBean" class="org.kewei.service.BeanScopeModel" scope="singleton">
    <constructor-arg index="0" value="singleton"/>
</bean>
代码语言:javascript
复制
public class BeanScopeModel {
    public BeanScopeModel(String beanScope) {
        System.out.println(String.format("create BeanScopeModel,{sope=%s},{this=%s}", beanScope, this));
    }
}

上面构造方法中输出了一段文字,可以根据输出来看一下这个bean什么时候创建的,是从容器中获取bean的时候创建的还是容器启动的时候创建的。执行代码如下所示:

代码语言:javascript
复制
public class ScopeTest {
    ClassPathXmlApplicationContext context;

    @Before
    public void before() {
        System.out.println("spring容器准备启动.....");
        //1.bean配置文件位置
        String beanXml = "classpath:/test.xml";
        //2.创建ClassPathXmlApplicationContext容器,给容器指定需要加载的bean配置文件
        this.context = new ClassPathXmlApplicationContext(beanXml);
        System.out.println("spring容器启动完毕!");
    }

    /**
     * 单例bean
     */
    @Test
    public void singletonBean() {
        System.out.println("---------单例bean,每次获取的bean实例都一样---------");
        System.out.println(context.getBean("singletonBean"));
        System.out.println(context.getBean("singletonBean"));
        System.out.println(context.getBean("singletonBean"));
    }
}

上面代码中before方法上面有@Before注解,这个是junit提供的功能,这个方法会在所有@Test标注的方法之前去执行,before方法中我们对容器进行初始化,并且在容器初始化前后输出了一段文字。

上面代码中,singletonBean方法中,3次获取singletonBean对应的bean都是同一个Bean。执行结果如下所示:

代码语言:javascript
复制
spring容器准备启动.....
反射通过调用构造函数进行实例创建...
create BeanScopeModel,{sope=singleton},{this=org.kewei.service.BeanScopeModel@704921a5}
spring容器启动完毕!
---------单例bean,每次获取的bean实例都一样---------
org.kewei.service.BeanScopeModel@704921a5
org.kewei.service.BeanScopeModel@704921a5
org.kewei.service.BeanScopeModel@704921a5

注解形式

直接在bean对象方法上增加@Scope注解即可,在不指定@Scope的情况下,所有的bean都是单实例的bean,而且是饿汉式单例加载(容器启动实例就创建好了,就像刚才我们通过Xml演示的一样。)

代码语言:javascript
复制
@Component
@Scope
public class BeanScopeAnnotationModel {
    public BeanScopeAnnotationModel() {
        System.out.println(String.format("create BeanScopeModel,{this=%s}", this));
    }
}
代码语言:javascript
复制
public class ScopeAnnotationTest {
    AnnotationConfigApplicationContext context;

    @Before
    public void before() {
        System.out.println("spring容器准备启动.....");
        this.context = new AnnotationConfigApplicationContext(BeanScopeAnnotationModel.class);
        System.out.println("spring容器启动完毕!");
    }

    /**
     * 单例bean
     */
    @Test
    public void singletonBean() {
        System.out.println("---------单例bean之Annotation,每次获取的bean实例都一样---------");
        System.out.println(context.getBean("beanScopeAnnotationModel"));
        System.out.println(context.getBean("beanScopeAnnotationModel"));
        System.out.println(context.getBean("beanScopeAnnotationModel"));
    }
}

运行结果如下

代码语言:javascript
复制
spring容器准备启动.....
create BeanScopeModel,{this=org.kewei.service.BeanScopeAnnotationModel@2e377400}
spring容器启动完毕!
---------单例bean之Annotation,每次获取的bean实例都一样---------
org.kewei.service.BeanScopeAnnotationModel@2e377400
org.kewei.service.BeanScopeAnnotationModel@2e377400
org.kewei.service.BeanScopeAnnotationModel@2e377400

结论

从输出中得到2个结论

  • 前3行的输出可以看出,IOC容器启动过程中创建了BeanScopeModel对象,调用了其构造方法,说明这个bean实例在容器启动过程中就已经创建好了,并放在容器中,等调用者进行使用,这也是充分证明了IOC的解耦和反射机制。
  • 最后3行输出的是一样的,说明返回的是同一个bean对象。

单例bean使用注意

单例bean是整个项目应用所共享的,所以需要考虑到线程安全的问题,比如我们在日常开发过程中在创建Controller的时候,这个就是单例Bean,其实默认都是单例的,如果我们在其中创建了一些变量,那么这些变量实际上就变成共享的了,如果该controller被很多线程同时访问,这些线程并发去修改controller中的共享变量,可能会发生多线程情况下数据的不一致问题,所以使用的时候需要特别注意,诚然我们可以采用加锁、ThreadLocal、封装成实体类等方式进行避免。

prototype

如果scope被设置为prototype类型的了,表示这个bean是多例的,通过容器每次获取的bean都是不同的实例,每次获取都会重新创建一个bean实例对象。

代码语言:javascript
复制
  <bean id="prototypeBean" class="org.kewei.service.BeanScopeModel" scope="prototype">
        <constructor-arg index="0" value="prototype"/>
  </bean>
代码语言:javascript
复制
    @Test
    public void prototypeBean() {
        System.out.println("---------多例bean,每次获取的bean实例都不一样---------");
        System.out.println(context.getBean("prototypeBean"));
        System.out.println(context.getBean("prototypeBean"));
        System.out.println(context.getBean("prototypeBean"));
    }
代码语言:javascript
复制
"D:\Program Files\Java\jdk1.8.0_131\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2023.2.1\lib\idea_rt.jar=52602:D:\Program Files\JetBrains\IntelliJ IDEA 2023.2.1\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2023.2.1\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2023.2.1\plugins\junit\lib\junit5-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2023.2.1\plugins\junit\lib\junit-rt.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;E:\WorkSpace\myproject\spring-mvc\target\classes;D:\maven\maven-repository\org\springframework\spring-context\5.2.6.RELEASE\spring-context-5.2.6.RELEASE.jar;D:\maven\maven-repository\org\springframework\spring-aop\5.2.6.RELEASE\spring-aop-5.2.6.RELEASE.jar;D:\maven\maven-repository\org\springframework\spring-beans\5.2.6.RELEASE\spring-beans-5.2.6.RELEASE.jar;D:\maven\maven-repository\org\springframework\spring-core\5.2.6.RELEASE\spring-core-5.2.6.RELEASE.jar;D:\maven\maven-repository\org\springframework\spring-jcl\5.2.6.RELEASE\spring-jcl-5.2.6.RELEASE.jar;D:\maven\maven-repository\org\springframework\spring-expression\5.2.6.RELEASE\spring-expression-5.2.6.RELEASE.jar;D:\maven\maven-repository\junit\junit\4.13.2\junit-4.13.2.jar;D:\maven\maven-repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 org.kewei.ScopeTest,prototypeBean
spring容器准备启动.....
反射通过调用构造函数进行实例创建...
create BeanScopeModel,{sope=singleton},{this=org.kewei.service.BeanScopeModel@704921a5}
spring容器启动完毕!
---------多例bean,每次获取的bean实例都不一样---------
create BeanScopeModel,{sope=prototype},{this=org.kewei.service.BeanScopeModel@2f8f5f62}
org.kewei.service.BeanScopeModel@2f8f5f62
create BeanScopeModel,{sope=prototype},{this=org.kewei.service.BeanScopeModel@1068e947}
org.kewei.service.BeanScopeModel@1068e947
create BeanScopeModel,{sope=prototype},{this=org.kewei.service.BeanScopeModel@7dc222ae}
org.kewei.service.BeanScopeModel@7dc222ae

注解形式

指定@Scope为 prototype 表示为多实例的,而且还是懒汉模式加载(IOC容器启动的时候,并不会创建对象,而是在第一次使用的时候才会创建)。

代码语言:javascript
复制
@Component
@Scope(value = "prototype")
public class BeanScopeAnnotationModel {
    public BeanScopeAnnotationModel() {
        System.out.println(String.format("create BeanScopeModel,{this=%s}", this));
    }
}
代码语言:javascript
复制
    @Test
    public void prototypeBean() {
        System.out.println("---------多例bean之Annotation,每次获取的bean实例都不一样---------");
        System.out.println(context.getBean("beanScopeAnnotationModel"));
        System.out.println(context.getBean("beanScopeAnnotationModel"));
        System.out.println(context.getBean("beanScopeAnnotationModel"));
    }

运行结果

代码语言:javascript
复制
"D:\Program Files\Java\jdk1.8.0_131\bin\java.exe" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:53144,suspend=y,server=n -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2023.2\captureAgent\debugger-agent.jar -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2023.2.1\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2023.2.1\plugins\junit\lib\junit5-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2023.2.1\plugins\junit\lib\junit-rt.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;E:\WorkSpace\myproject\spring-mvc\target\classes;D:\maven\maven-repository\org\springframework\spring-context\5.2.6.RELEASE\spring-context-5.2.6.RELEASE.jar;D:\maven\maven-repository\org\springframework\spring-aop\5.2.6.RELEASE\spring-aop-5.2.6.RELEASE.jar;D:\maven\maven-repository\org\springframework\spring-beans\5.2.6.RELEASE\spring-beans-5.2.6.RELEASE.jar;D:\maven\maven-repository\org\springframework\spring-core\5.2.6.RELEASE\spring-core-5.2.6.RELEASE.jar;D:\maven\maven-repository\org\springframework\spring-jcl\5.2.6.RELEASE\spring-jcl-5.2.6.RELEASE.jar;D:\maven\maven-repository\org\springframework\spring-expression\5.2.6.RELEASE\spring-expression-5.2.6.RELEASE.jar;D:\maven\maven-repository\junit\junit\4.13.2\junit-4.13.2.jar;D:\maven\maven-repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 org.kewei.ScopeAnnotationTest,prototypeBean
Connected to the target VM, address: '127.0.0.1:53144', transport: 'socket'
spring容器准备启动.....
spring容器启动完毕!
---------多例bean之Annotation,每次获取的bean实例都不一样---------
create BeanScopeModel,{this=org.kewei.service.BeanScopeAnnotationModel@71c3b41}
org.kewei.service.BeanScopeAnnotationModel@71c3b41
create BeanScopeModel,{this=org.kewei.service.BeanScopeAnnotationModel@1329eff}
org.kewei.service.BeanScopeAnnotationModel@1329eff
create BeanScopeModel,{this=org.kewei.service.BeanScopeAnnotationModel@6497b078}
org.kewei.service.BeanScopeAnnotationModel@6497b078
Disconnected from the target VM, address: '127.0.0.1:53144', transport: 'socket'

结论

输出中可以看出,容器启动过程中并没有去创建多例BeanScopeModel对象,只有单例的Bean被创建。3次获取prototypeBean得到的都是不同的实例,每次获取的时候才会去调用构造方法创建bean实例。

多例bean使用注意

多例bean每次获取的时候都会重新创建,如果这个bean比较复杂,创建时间比较长,会影响系统的性能,这个地方需要注意。

下面要介绍的3个:request、session、application都是在spring-web环境中才会有的,也就是我们后面会写的SpringMVC的时候会遇到。

request

当一个bean的作用域为request,表示在一次http请求中,一个bean对应一个实例;对每个http请求都会创建一个bean实例,request结束的时候,这个bean也就结束了,request作用域用在spring容器的web环境中,spring中有个web容器接口WebApplicationContext,这个里面对request作用域提供了支持,配置方式:

代码语言:javascript
复制
<bean id="" class="" scope="request" />

session

这个和request类似,也是用在web环境中,session级别共享的bean,每个会话会对应一个bean实例,不同的session对应不同的bean实例,springmvc中我们再细说吧。

代码语言:javascript
复制
<bean id="" class="" scope="session" />

application

全局web应用级别的作用域,也是在web环境中使用的,一个web应用程序对应一个bean实例,通常情况下和singleton效果类似的,不过也有不一样的地方,singleton是每个spring容器中只有一个bean实例,一般我们的程序只有一个spring容器,但是,一个应用程序中可以创建多个spring容器,不同的容器中可以存在同名的bean,但是sope=aplication的时候,不管应用中有多少个spring容器,这个应用中同名的bean只有一个。

代码语言:javascript
复制
<bean id="" class="" scope="application" />

1、总结

  1. spring容器自带的有2种作用域,分别是singleton和prototype;还有3种分别是spring web容器环境中才支持的request、session、application。
  2. singleton是spring容器默认的作用域,一个spring容器中同名的bean实例只有一个,多次获取得到的是同一个bean;单例的bean需要考虑线程安全问题。
  3. prototype是多例的,每次从容器中获取同名的bean,都会重新创建一个;多例bean使用的时候需要考虑创建bean对性能的影响。
  4. 一个应用中可以有多个spring容器。
  5. 使用singleton单例,采用饿汉加载(容器启动,Bean实例就创建好了)
  6. 使用prototype多例,采用懒汉加载(IOC容器启动的时候,并不会创建对象实例,而是在第一次使用的时候才会创建)
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-11-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 可为编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 戳上方蓝字“可为编程” 点击右上角选择“设为星标”,好文不错过!
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档