• Bean 的作用域和生命周期


    引言

    在实际工作中或者日常做项目的时候,我们都会更加注重 " 如何使用 spring 框架 ",也就是将核心放在 【存 / 取 spring 中的对象】这件事情上。

    然而,如果我们需要更加深入理解一个 bean 对象 " 在存取的过程中发生了什么 ",就需要明白它的作用域和生命周期。

    一、Bean 的作用域

    引入案例

    User 类:

    public class User{
        public int id;
        public String name;
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    UserBean 类,用来往 spring 中存放 User对象。

    @Controller
    public class UserBean {
        @Bean(name = "user3")
        public User getUser3() {
            User user = new User();
            user.id = 3;
            user.name = "小明";
            return user;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    BeanScpoe1 类,进行 user3 对象注入,之后拿一个新的 user 对象接收。

    @Component
    public class BeanScope1 {
        @Autowired
        private User user3;
    
        public User hello() {
            User user = user3;
            user.name = "小红";
            return user;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    BeanScope2 类,进行 user3 对象注入。

    @Component
    public class BeanScope2 {
    
        @Autowired
        private User user3;
    
        public User hello() {
            return user3;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    Run 启动类:

    public class Run {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
            BeanScope1 beanScope1 = context.getBean( "beanScope1", BeanScope1.class);
            User user = beanScope1.hello();
            System.out.println( " beanScope1: " + user);
    
            BeanScope2 beanScope2 = context.getBean("beanScope2", BeanScope2.class);
            User user2 = beanScope2.hello();
            System.out.println(" beanScope2: " + user2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    输出结果:

    1-1

    描述一下上述的代码逻辑:

    我们在最初的时候,设计一个 User 类,里面放着 " id " 和 " name " 字段;之后,我们预期将 User 类的对象放入 spring 容器中,所以就设计了一个 UserBean 类,最终,我们将 " id = 3 " 和 " name = 小明 " 的对象放入了 spring 容器中,bean 对象名字为 user3.

    接着,我们又设计了一个 BeanScpoe1 类,进行了 user3 对象注入,我们期望通过一个新的 user 对象接收,并为新的对象设置 " name = 小红 ",然而,不巧的是,这一操作,直接影响到了 spring 容器中的 user3 对象。

    所以,后面我们在 BeanScope2 类,注入 user3 对象的时候,原来 " name = 小明 " 就变成了 " name = 小红 "。

    分析原因

    造成以上的结果,其原因是 Bean 默认情况下是单例状态,bean 对象只有一个,也就是说,所有类使用的其实是同一个 bean 对象。

    而上面的这种单例状态的情况,实际上只是 Bean 作用域的其中一种模式。

    Bean 的作用域

    Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式。

    比如 singleton 单例作用域,就表示 bean 对象在整个 Spring 中只有一份,它是全局共享的,那么当其他人修改了这个值之后,那么另一个人读取到的就是被修改的值。

    Bean 的六种作用域

    在普通的 Spring 项目中只有 前两种,而 后四种状态是 Spring MVC 中的值。

    1. singleton:单例模式 ( 默认 )

    单例模式下,获取 bean 对象 / 注入 bean 对象,都是针对于同一个对象。

    2. prototype:原型模式 ( 多例模式 )

    原型模式实际上和默认的单例模式正好相反。

    原型模式下,获取 bean 对象 / 注入 bean 对象,都是新的对象实例。也就是说,每次对该作用域下的 Bean 请求都会创建新的实例。

    3. request:请求作用域 ( Spring MVC )

    当前模式下,每次 HTTP 请求都会创建新的 bean 对象,类似于 " prototype " ,只不过,此模式限定在 Spring MVC 中使用。

    4. session:会话作用域 ( Spring MVC )

    在一个 HTTP Session 中,定义一个 bean 对象,这就和之前的 Servlet 提供的会话机制差不多。比方说它用到的一个典型场景:记录一个用户的登录信息。

    5. application:全局作用域 ( Spring MVC )

    在一个 HTTP Servlet Context 中,定义一个 bean 对象。

    6. websocket:HTTP WebSocket作用域 ( Spring WebSocket )

    在一个 HTTP WebSocket 中,定义一个 bean 对象。

    更改作用域

    由于当前的是一个普通的 spring 项目,我们就将 " 单例模式 " 换成 " 原型模式 ",看一下效果。

    对于上面的 UserBean 类进行改变,也就是让 User 对象存入 spring 容器时,添加一个 " @Scope " 注解。其他的代码不需要改动。

    @Controller
    public class UserBean {
    
        @Bean(name = "user3")
        @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
        public User getUser3() {
            User user = new User();
            user.id = 3;
            user.name = "小明";
            return user;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    输出结果:

    1-2

    在原型模式下,获取 bean 对象 / 注入 bean 对象,都是新的对象实例。也就是说,BeanScope1 类无论怎么修改,都不影响 BeanScope2 类拿 user3 对象。

    格式

    // 两种写法
    @Scope("prototype")
    
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    
    • 1
    • 2
    • 3
    • 4

    二、Bean 的生命周期

    所谓的生命周期指的是一个对象从诞生到销毁的整个生命过程,我们把这个过程就叫做一个 bean 对象的生命周期。

    一个 bean 对象的生命周期大致分为下面的五个部分:

    1. 实例化 ( 给 bean 分配内存空间 )

    2. 设置属性 ( 对象注入 )

    3. 初始化

    (1) 执行各种通知 ( 执行各种 Aware )
    (2) 执行初始化的前置方法
    (3) 执行构造方法,两种执行方式,一种是执行 " @PostConstruct " , 另一种是执行 " init-method " .
    (4) 执行初始化的后置方法

    4. 使用 Bean

    5. 销毁Bean

    (1) " @PreDestroy "
    (2) 重写 DisposableBean 接口方法
    (3) destroy - method

    2-1

    例子

    我们可以将一个 bean 对象想象成一个买房到卖房的过程。

    1. 先买房(实例化,从无到有)
    2. 装修(设置属性)
    3. 买家电,如洗衣机、冰箱、电视、空调等([各种]初始化)
    4. 入住(使用 Bean)
    5. 卖出去(Bean 销毁)
  • 相关阅读:
    【揭秘Vue】nextTick的神秘面纱:原理与作用一览无余!
    使用Pega进行一个简单的RPA程序开发
    presto插件机制揭秘:探索无限可能的数据处理舞台
    关于 /lib/modules/**内核版本号**/ build 和 /source
    百度智能云千帆大模型平台再升级,SDK版本开源发布!
    Open3D 生成空间圆点云
    leetcode:1106. 解析布尔表达式【栈的应用】
    【概率论与数理统计(研究生课程)】知识点总结1(概率论基础)
    在上一篇文章的基础上,增加“刷新页面,使form表单数据不发生变化”
    【智能客服】聊天机器人的过去与未来
  • 原文地址:https://blog.csdn.net/lfm1010123/article/details/127066933