1、Bean作用域问题

通过一个案例来看Bean的作用域。假设有一个公共的Bean对象,提供给A类和B类使用,然而在A类使用的过程中,修改了Bean的数据,导致B类在使用Bean的时候产生了预期之外的逻辑错误。

预期结果是公共Bean可以在各自类中被修改,但不能影响其他类使用(使用时还是Bean的初始数据)。

公共Bean:

@Component
public class Users {
    @Bean
    public User user1(){
        User user = new User();
        user.setName("zhangsan");//注意 初始化为 zhangsan
        user.setId(1);
        return user;
    }
}

User对象:

public class User {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

A类使用时,修改了数据:把name修改成了Lisi

@Controller
public class AController {
    @Autowired
    private User user;
    public User getUser1(){
        System.out.println("Bean 原 name :"+user.getName());
        user.setName("Lisi");//A类修改了Bean原生数据
        return user;
    }
}

B类再去使用公共Bean时:

@Controller
public class BController {
    @Autowired
    private User user1;

    public User getUser1(){
        User user = user1;
        return user;
    }
}

打印A和B类使用公共Bean 的值:

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.config.xml");
        
        AController aController = context.getBean(AController.class);
        System.out.println("A类修改后 name :"+aController.getUser1().toString());
        
        BController bController = context.getBean(BController.class);
        System.out.println("B读取到的 name :"+bController.getUser1().toString());
    }
}

运行结果:

在这里插入图片描述

原因分析:

以上运行结果的原因是:因为Bean默认情况下是单例模式(singleton),也就是所有人使用的都是同一个Bean对象。因为使用单例模式可以很大程度上提高性能,所以在spring中Bean的作用域默认也是singleton单例模式。

2、作用域定义

程序中变量的可用范围就叫做作用域,或者说在源代码中定义某个变量在某个区域就叫做作用域。而Bean的作用域是指Bean在Spring整个框架中的某种行为模式,比如singleton单例作用域,就表示Bean在spring中只有一份,全局共享的,当A修改了这个值后,那么B再使用时就是修改后的值。

2.1、Bean的6种作用域

Spring容器在初始化一个Bean对象时,会指定该实例的作用域。spring有6种作用域,最后4种是基于Spring MVC生效的:

1.  singleton:单例模式
2.  prototype:原型作用域(多例作用域)
3.  request:请求作用域
4.  session:会话作用域
5.  application:全局作用域
6.  websocket:HTTP  WebSocket 作用域

注意后4种是SpringMVC中的值,在普通的Spring项目中只有前两种。

singleton 单例模式

  • 该作⽤域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过 applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同⼀个对象。
  • 场景:通常⽆状态的Bean使⽤该作⽤域。⽆状态表示Bean对象的属性状态不需要更新。
  • 备注:Spring默认选择该作⽤域。

prototype 原型作用域

  • 描述:每次对该作用域下的Bean的请求都会创建一个新的实例:获取Bean(即通过 applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同新的对象。
  • 场景:通常有状态的Bean使用该作用域。

request 请求作用域

  • 描述:每次http请求会创建新的Bean实例,类似于prototype
  • 场景:⼀次http的请求和响应的共享Bean
  • ⼀次http的请求和响应的共享Bean

session 会话作用域

  • 描述:在⼀个http session中,定义⼀个Bean实例

  • 场景:⽤户回话的共享Bean, ⽐如:记录⼀个⽤户的登陆信息

    备。

  • 备注:限定SpringMVC中使⽤

application 全局作用域(了解)

  • 描述:在⼀个http servlet Context中,定义⼀个Bean实例。
  • 场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息。
  • 备注:限定SpringMVC中使⽤。

websocket

  • 描述:在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bean实例。
  • 场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息头。第⼀次初始化后,知道WebSocket结束都是同一个Bean。
  • 备注:限定Spring WebSocket中使⽤。

单例作用域 vs 全局作用域

  • singleton 是 Spring Core 的作⽤域;application 是 Spring Web 中的作⽤域;
  • singleton 作⽤于 IoC 的容器,⽽ application 作⽤于 Servlet 容器。

2.设置作用域

@Scope既可以修饰方法也可以修饰类

  1. 使用@Scope标签设置Bean的作用域: 设置为 prototype 全局作用域
@Component
public class Users {
    @Scope("prototype")//设置为 prototype 全局作用域
    @Bean
    public User user1(){
        User user = new User();
        user.setName("zhangsan");//注意 初始化为 zhangsan
        user.setId(1);
        return user;
    }
}

运行结果:此时A修改数据,不会影响B读取的数据。

在这里插入图片描述

  1. 使用枚举设置@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class Users {
    //@Scope("prototype")
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Bean
    public User user1(){
        User user = new User();
        user.setName("zhangsan");//注意 初始化为 zhangsan
        user.setId(1);
        return user;
    }
}

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6TiFDyiP-1686991444637)(C:\Users\COFFEEWEN\AppData\Roaming\Typora\typora-user-images\image-20230616150256693.png)]

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐