Bean的作用域

在Spring Framework中,总共定义了6中bean的作用域,其中4种作用域只有当应用为web应用时才生效,并且Spring还支持自定义作用域。

ScopeDescription
singleton(默认作用域)在每个Spring IoC容器种,一个bean定义对应只会有唯一的一个bean实例
prototype一个bean定义可以有多个bean实例
request一个bean定义对应于单个HTTP请求的生命周期。也就是说,每个HTTP请求都有一个bean实例,且该实例仅在这个HTTP请求的生命周期里生效。该作用域仅仅适用于WebApplicationContext环境。
session一个bean定义对应于单个Session的生命周期。也就是说,每个HTTP Session都有一个bean实例,且该实例仅在这个HTTP Session的生命周期里有效。该作用域仅适用于WebApplicationContext环境。
application一个bean定义对应于单个ServletContext的生命周期。该作用域仅适用于WebApplicationContext环境。
wrbsocket一个bean定义对于单个webSocket的生命周期。该作用域仅适用于WebApplicationContext环境。

singleton

Singleton作用域指的是在整个Spring容器中,只会存在一个Bean实例。所有的请求都会共享这个唯一的实例。这意味着,无论在何时何地请求该Bean,都会返回同一个实例。Singleton作用域是Spring框架默认的作用域,也是最常用的作用域之一。具体来说,当容器启动时,Spring会根据配置文件或者注解扫描等方式创建并初始化Singlenton Bean实例,并将其放入一个单例池中。当应用程序需要使用该Bean时,Spring容器会从单例池中返回该Bean的实例,而不是创建一个新的实例。因此,所有的请求都会共享同一个实例,避免了重复创建和销毁Bean实例的开销,提高了应用程序的性能和效率。

Singleton作用域的优点包括:

  1. 节省资源,提高性能:由于只有一个实例,避免了重复创建和销毁Bean实例的开销,可以节省系统资源,提高应用程序的性能和效率
  2. 全局唯一性:确保整个应用程序中只存在一个实例,避免了在多个实例之间共享数据时可能出现数据不一致的问题
  3. 可以在多个容器之间共享实例:由于Singleton Bean实例是在整个应用程序中唯一的,所以可以在多个Spring容器之间共享实例,方便应用程序的开发和部署

Singleton作用域也存在一些缺点:

  1. 线程安全问题:由于Singleton作用域的Bean实例是全局唯一的,所以在多线程环境下可能会出现线程安全问题
  2. 生命周期管理问题:由于Singleton的Bean实例是在Spring容器启动时创建的,而且只有在容器关闭时才会被销毁,所以无法手动控制Bean实例的创建和销毁
  3. 对象状态管理问题:如果Singleton作用域的Bean实例存在状态变化,可能会导致在不同请求之间产生互相干扰的情况,从而影响程序的正确性

代码示例

package com.powernode.Service;

import com.powernode.Dao.UserDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * @Author: 
 * @Description: TODO
 * @DateTime: 2024/9/19 15:19
 */
public class UserServiceImpl implements UserService{

    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    public UserDao userDao;

    public void setUserDao(UserDao userDao){
        System.out.println("userDao的set方法执行了");
        this.userDao = userDao;
    }

    @Override
    public void add() {
        logger.info("add():userDao:{}", userDao);
        userDao.add();
    }

    @Override
    public void update() {
        logger.info("update():userDao:{}", userDao);
        userDao.update();
    }
}
<?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">
   
  <!--
        目前来说:scope属性有两个值
            第一个值:singleton 单例(默认情况下就是单例的。)
            第二个值:prototype 原型/多例

        其实scope属性有多个值:
            例如:request session
            但是request session要求项目必须是一个web应用。
  					当你引入springmvc框架的时候,这两个值就可以使用了。
            request:一次请求当中一个bean
            session:一次会话中只有一个bean
    -->
    <bean id="userServiceBean" class="com.powernode.Service.UserServiceImpl" scope="singleton">
        <property name="userDao" ref="userDaoBean"/>
    </bean>

    <bean id="userDaoBean" class="com.powernode.Dao.UserDaoImpl" />
</beans>
@org.junit.Test
public void singletonTest(){
    /*
     *  Spring默认情况下是如何管理这个Bean的:
     *      默认情况下Bean是单例的。(单例:singleton)
     *      在Spring上下文初始化的时候实例化。
     *      每一次调用getBean()方法的时候,都返回那个单例的对象。
     */
    ApplicationContext beans = new ClassPathXmlApplicationContext("spring-scope-bean.xml");

    UserService userServiceBean1 = (UserService) beans.getBean("userServiceBean");
    System.out.println(userServiceBean1);

    UserService userServiceBean2 = (UserService) beans.getBean("userServiceBean");
    System.out.println(userServiceBean2);
}

执行结果

在取消getBean方法之后,**setUserDao**的打印语句仍然执行了,说明单例模式下,Bean对象的创建是在初始化Spring上下文的时候就完成了。

取消getBean之后

prototype

Prototype作用域时Spring框架中的一种Bean作用域,它与Singleton作用域不同,每次请求该Bean时,都会** 创建一个新的实例**。也就是说,**每次Spring容器中获取Prototype Bean时,都会得到一个新的实例对象**。具体来说,当容器启动时,Spring会根据配置文件或注解扫描等方式创建并初始化Prototype Bean实例,但是不会将其放入单例池中,而是在每次请求该Bean时,都会重新创建一个新的实例,并返回给调用着。因此,每个调用者都会得到一个全新的Bean实例,彼此之间没有任何关联。

Prototype作用域的优点

  1. 灵活性:每次请求时都会创建一个新的实例,不同调用者得到的实例对象彼此之间没有任何关联,因此可以更灵活地控制Bean的创建和销毁
  2. 对象状态隔离:由于每个调用者都会得到一个全新的Bean实例,所以不同调用者之间不会互相干扰,从而避免了状态共享可能带来的问题
  3. 无线程安全问题:由于每个调用者都会得到一个全新的Bean实例,所以在多线程环境下不会出现线程安全问题

Prototype作用域的缺点包括

  1. 性能开销:由于每次请求时都会创建一个新的实例,所以会消耗更多的系统资源和时间,可能会影响应用程序和性能
  2. 生命周期管理问题:由于Prototype作用域的Bean实例不是单例的,所以无法通过Spring容器来管理生命周期,程序员需要手动控制Bean实例的创建和销毁
  3. 对象共享问题:由于Prototype作用域的Bean实例不是单例的,如果一个Bean实例被多个对象引用,可能会出现对象共享问题

代码示例

```xml <?xml version="1.0" encoding="UTF-8"?>
<bean id="userServiceBean" class="com.powernode.Service.UserServiceImpl" scope="prototype">
    <property name="userDao" ref="userDaoBean"/>
</bean>

<bean id="userDaoBean" class="com.powernode.Dao.UserDaoImpl" />
```
@org.junit.Test
public void singletonTest(){
    ApplicationContext beans = new ClassPathXmlApplicationContext("spring-scope-bean.xml");

    UserService userServiceBean1 = (UserService) beans.getBean("userServiceBean");
    System.out.println(userServiceBean1);

    UserService userServiceBean2 = (UserService) beans.getBean("userServiceBean");
    System.out.println(userServiceBean2);
}

执行结果

在多例模式下,取消getBean之后**setUserDao的打印语句并没有执行,所以可以推断出在初始化Spring上下文的时候,并没有创建Bean对象,而是在获取Bean对象的时候才创建。**

取消getBean之后

自定义scope

> 注意:自定义scope是不能够覆盖Singleton和Prototype作用域的 >

自定义一个Scope,线程级别的Scope,在同一个线程中,获取的Bean都是同一个。跨线程则是不同的对象:(以下内容作为了解)

  • 第一步:自定义Scope。(实现Scope接口)
    • spring内置了线程范围的类:org.springframework.context.support.SimpleThreadScope,可以直接用。
  • 第二步:将自定义的Scope注册到Spring容器中。
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
  <property name="scopes">
    <map>
      <entry key="myThread">
        <bean class="org.springframework.context.support.SimpleThreadScope"/>
      </entry>
    </map>
  </property>
</bean>
  • 第三步:使用scope
<bean id="sb" class="com.rui.spring6.beans.SpringBean" scope="myThread" />

测试程序

@Test
public void testThreadScope() {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-scope.xml");
    SpringBean sb = ctx.getBean("sb", SpringBean.class);
    System.out.println(sb);
    SpringBean sb1 = ctx.getBean("sb", SpringBean.class);
    System.out.println(sb1);

    // 启动一个新的线程
    new Thread(new Runnable() {
        @Override
        public void run() {
            SpringBean sb2 = ctx.getBean("sb", SpringBean.class);
            System.out.println(sb2);
            SpringBean sb3 = ctx.getBean("sb", SpringBean.class);
            System.out.println(sb3);
        }
    }).start();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值