Bean 的六种作用域

观前提示:本篇博客演示使用的 IDEA 版本为2021.3.3版本,使用的是Java8(又名jdk1.8)

前端使用VSCode(Visual Studio Code1.78.2)

电脑使用的操作系统版本为 Windows 10


目录

前言

Bean

Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作⽤域。

1.  singleton:单例作用域

1.1 原因

2.  prototype:原型作用域(多例作用域)

2.1 设置作用域

3.  request:请求作用域

4.  session:回话作用域

5.  application:全局作用域

6. websocket:HTTP WebSocket 作⽤域


 前言

本篇博客将对 Spring 里面的 Bean 的六种作用域进行介绍,重点介绍单例模式与原型模式, 后面四种是 Spring MVC 中的值, 后面的博客再介绍

Bean

Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作⽤域。

1.  singleton:单例作用域

先来看一个栗子

先创建一个 com.java.demo.enity 的包 里面放一个 user 类, 类里面有 id 和 name, 使用 lombok 来处理 Get, Set 以及 ToString

package com.java.demo.enity;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class User {
    private int id;
    private String name;
}

再建立一个 com.java.demo.component 的包, 里面放 UserBeans, 对 User 里面的数据进行初始化

package com.java.demo.component;

import com.java.demo.enity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class UserBeans {

    @Bean
    public User user() {
        // 伪代码, 用来初始化 user 类里面的 name 和 id
        User user = new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
}

再建立一个 com.java.demo.controller 的包, 建立 userController 类, 在局部变量修改 Bean, 分别打印局部变量和全局变量,查看结果

package com.java.demo.controller;

import com.java.demo.enity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {
    //依赖注入,把 User 类型的 Bean 注入 UserController 属性中。
    @Autowired
    private User user;

    public void printUser() {
        // 先打印一次 user, 看看结果是什么
        System.out.println(user);

        //在局部变量里面对 Bean 进行修改
        User myUser = user;
        myUser.setName("李四");
        //查看局部变量修改后局部变量的打印结果
        System.out.println("myUser -> " + myUser);
        //查看局部变量修改后全局变量的打印结果
        System.out.println("user -> " + user);
    }
}

再建立一个 com.java.demo.controller 的包, 建立 userController2 类 , 专门打印 Bean

package com.java.demo.controller;

import com.java.demo.enity.User;
import org.springframework.stereotype.Controller;

import javax.annotation.Resource;

@Controller
public class UserController2 {
    @Resource
    private User user;

    public void printUser2() {
        // 专门打印看看能不能拿到 张三
        System.out.println("user -> " + user);
    }
}

在创建一个 App 类,用来进行启动

import com.java.demo.controller.UserController;
import com.java.demo.controller.UserController2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spring-config.xml");
        UserController controller = context.getBean("userController", UserController.class);
        controller.printUser();
        UserController2 controller2 = context.getBean("userController2", UserController2.class);
        controller2.printUser2();
    }
}

上述代码总共进行了四次打印, 预估结果, 第一次打印:张三(全局变量), 第二次打印:李四(局部变量修改), 第三次打印:张三(局部变量修改不会影响全局变量)

第四次打印: 张三

但是结果真的是这样吗?

 

结果除了第一个符合预期, 后面的全部的不符合预期,这是为什么? 明明只是修改了局部变量,他却连全局变量都被修改了

1.1 原因

因为 Spring默认是 单例模式, 翻译就是整个 Spring 里面的 Bean 都是同一个对象, 因为 Bean 只有一份,修改一次之后(不论修改的是全局还是局部),都是永久修改

为什么他默认是单例模式呢? , 当然是为了提高性能, 他只需要初始化一次, 同样 JVM 的类加载也只需要执行一次


2.  prototype:原型作用域(多例作用域)

不想要单例模式可以进行修改, 例如修改成多例作用域

2.1 设置作用域

要在设置的时候就对他进行修改,不能获取到了再修改, 所以是在 UserBeans

两种写法

1. 直接设置值:@Scope("prototype")

package com.java.demo.component;

import com.java.demo.enity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
public class UserBeans {
    @Bean
    @Scope("prototype")
    public User user() {
        // 伪代码, 用来初始化 user 类里面的 name 和 id
        User user = new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
}

 

2. 使⽤枚举设置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

package com.java.demo.component;

import com.java.demo.enity.User;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
public class UserBeans {
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public User user() {
        // 伪代码, 用来初始化 user 类里面的 name 和 id
        User user = new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
}

 

更加推荐使用第二种写法, 因为第一个错了不容易发现,第二个错了 idea 会提示


3.  request:请求作用域

官⽅说明:Scopes a single bean definition to the lifecycle of a single HTTP request. That is,
each HTTP request has its own instance of a bean created off the back of a single bean
definition. Only valid in the context of a web-aware Spring ApplicationContext.
描述:每次http请求会创建新的Bean实例,类似于prototype
场景:⼀次http的请求和响应的共享Bean
备注:限定SpringMVC中使⽤


4.  session:回话作用域

官⽅说明:Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in
the context of a web-aware Spring ApplicationContext.
描述:在⼀个http session中,定义⼀个Bean实例
场景:⽤户回话的共享Bean, ⽐如:记录⼀个⽤户的登陆信息
备注:限定SpringMVC中使⽤


5.  application:全局作用域

官⽅说明:Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in
the context of a web-aware Spring ApplicationContext.
描述:在⼀个http servlet Context中,定义⼀个Bean实例
场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息
备注:限定SpringMVC中使⽤


6. websocket:HTTP WebSocket 作⽤域

官⽅说明:Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the
context of a web-aware Spring ApplicationContext.
描述:在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bean实例
场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息头。第⼀
次初始化后,直到WebSocket结束都是同⼀个Bean。
备注:限定Spring WebSocket中使⽤
 


本文完,感谢观看,不足之处请在评论区指出 !

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值