Bean 的作用域和生命周期

目录

一、 Bean 的作用域

1. 安装Lombok插件

1.1 Lombok 简介

1.2 Lombok 安装

 2. 创建一个 User 对象,然后将 User 对象 存储到 Spring 容器中

2.1 创建User 对象

2.2 将User 对象存储到 Spring 中

2.3 修改 User 对象中的属性,然后看结果(两个对象是否都被修改)

2.4 获取 Bean 对象,看此对象是修改前的还是后的

2.5 运行结果:

2.6 结论:

3. Spring Bean 的6种作用域

3.1 作用域详解

3.2 代码设置 Bean 的作用域

 二、Spring 的执行流程和 Bean 的生命周期

1. 启动 spring 容器:

 2. 根据配置完成 Bean 对象的初始化:

 3. 将 Bean 对象存储到 Spring 容器中:

 4. 装配 Bean 的属性:

 3.2 Bean 的生命周期详解

 3.3 Bean 的初始化详解


前言

    上一篇文章已经介绍了如何更加简单的存储和获取 Bean 对象,那么 Bean 对象在 Spring 容器中的生命周期又是什么时期,是一直跟随 Spring 容器呢,还是可以随时创建和销毁的呢。所以这篇文章详细介绍一下 Bean 对象的作用域以及生命周期。

一、 Bean 的作用域

1. 安装Lombok插件

1.1 Lombok 简介

    Lombok能通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法。

1.2 Lombok 安装

(1)在pom.xml 配置文件中添加依赖:

<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.26</version>
            <scope>provided</scope>
        </dependency>

(2)在开发工具中安装 Lombok 插件:

 (3)Lombok 的使用:

 2. 创建一个 User 对象,然后将 User 对象 存储到 Spring 容器中

2.1 创建User 对象

package com.java.demo.enity;

import com.sun.javafx.binding.StringFormatter;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

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

2.2 将User 对象存储到 Spring 中

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 user = new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
}

2.3 修改 User 对象中的属性,然后看结果(两个对象是否都被修改)

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 {
    @Autowired
    private User user;

    public void printUser() {
        System.out.println(user);
        //修改 User
        User myUser = user;
        myUser.setName("良月初十" + myUser);
        System.out.println("myUser -> " + myUser);
        System.out.println("user -> " + user);
    }
}

2.4 获取 Bean 对象,看此对象是修改前的还是后的

package com.java.demo.controller;

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

import javax.annotation.Resource;

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

    public void printUser2() {
        System.out.println("user -> " + user);

    }
}
import com.java.demo.controller.UserController;
import com.java.demo.controller.UserController2;
import com.java.demo.enity.User;
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();
    }
}

2.5 运行结果:

2.6 结论:

    其实这个现象和SE 阶段学习的浅拷贝的现象是一样的,就是一个引用指向了另一个引用所指向的对象,之后改变对象的值,两个引用所指向的对象都会发生改变。

    我的预期是当一个 Bean 对象存储到了 Spring 容器中之后,一个程序猿拿到这个对象进行修改之后,当另一个程序猿再拿到这个对象还是原来的值,此时才是正常的情况。但是实际结果 这个 Bean 对象只有在整个 Spring 容器中只存储了一份,多个人进行修改的时候此时 这个对象也会随着一起改变。

    所以 Bean 的作用域:默认是单例模式(在整个Spring容器中只有一份)

    这也就意味着在任何一个地方修改 Bean 的值再去获取这个对象都是修改后的值。但是为啥又说默认是单例模式呢? 也就是 Bean 对象的作用域是可以进行设置的(不仅仅只有一种作用域)

3. Spring Bean 的6种作用域

3.1 作用域详解

     Spring 容器在初始化一个 Bean 对象的时候,同时会指定该对象的作用域,如果不进行设置,默认就是单例模式。

1. singleton :单例模式 (spring 要保证性能,此时进行初始化就只有一份对象)

    使用场景:一般用无状态的 Bean 适合使用该模式(无状态:Bean 对象的属性 在整个作用域中不需要被修改)

2. prototype:原型模式(多例作用域)(每次获取 Bean 时,都会重新生成一个 Bean 对象,所以此时性能不高)

    使用场景:一般有状态的 Bean 适合使用该模式

3. request:请求模式 (每次http请求都会创建一个 Bean 对象,类似于多例模式)

    使用场景:一次HTTP的请求和响应共享的 Bean 对象 适用于 该模式。

4. session:会话模式 (在一个HTTP session 中,使用一个 Bean 对象)(如:我进入了学校官网的教务系统,在退出教务系统之前我的多次http请求都是共享一个 Bean 对象的)

    使用场景:在一个用户会话中一般使用该种模式。

   (也就是说session模式对 Bean 对象的共享程度 比 request 模式 的 程度高)

5. application:全局模式 (一个 http servlet Context中会只使用一个 Bean 对象)

    如:

    如果只是一个context,此时就会使用一个 Bean 对象,如果有多个 context 对象,此时就是 一个 context 对象 对应一个 Bean 对象。

    使用场景:Web级别的应用的上下文信息。(获取一个Spring 容器)

6. websocket:HTTP WebSocket 模式 (每一个WebSocket的会话对应着一个 Bean 对象) 

    如:在网页版的电商软件的右下角的那个客服咨询就是一个 WebSocket会话(WebSocket 就是Socket的一个变种, 就是一个web 的长连接)

    使用场景:只适用于 Spring WebSocket 项目。

    注:前两种模式是 Spring 普通项目的模式,后四种模式适用于 Spring Web 项目中。

3.2 代码设置 Bean 的作用域

1. 直接设置值:@Scope("prototype")
2. 使⽤常量设置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

    注:设置作用域的时期是在Bean 对象刚存储进来的时候就需要设置好了作用域,而不是在获取Bean 对象的时候才 设置作用域。

    如下图所示:(直接设置值)

   (使⽤常量设置)

 二、Spring 的执行流程和 Bean 的生命周期

1. 启动容器

加载配置文件(类加载路径下的 spring-config.xml)

2. 根据配置完成 Bean 对象的初始化

扫描 com.java.demo 包(包含底下所有的子包),然后看哪些包中的类有:五大 类注解

3. 将 Bean 对象存储到 Spring 容器中如果有五大 类注解,就会将这个类实例化,存储到 Spring 容器中
4. 装配 Bean 的属性如果 Bean 对象需要使用其他 Bean 对象作为属性,可以使用 @Autowired 和 @Resource 注解来实现对象的装配

1. 启动 spring 容器:

 2. 根据配置完成 Bean 对象的初始化:

 3. 将 Bean 对象存储到 Spring 容器中:

 4. 装配 Bean 的属性:

 3.2 Bean 的生命周期详解

    生命周期:就是一个对象从有到无的一个整个的生命过程,称作一个对象的生命周期。

Bean 的生命周期大概分为5个部分:

1. 实例化 Bean 对象(给 bean 分配一块内存)
2. 设置属性(Bean 对象的注入和装配)
3. Bean 对象的初始化(这块下面进行详细介绍)
4. 使用 Bean 对象
5. 销毁 Bean 对象

    注:Bean 对象的实例化 和 初始化 不是一个概念,不可以混为一谈,实例化是分配内存空间的过程,初始化是通过执行自己的各种方法完成了构造一个完整的 Bean 对象的过程。

    所以实例化和设置属性的时机是在初始化 Bean 对象之前。

 (实例化就像是先买房,而初始化是装修,是需要先买房,再装修的)和JVM运行的流程是一样的关于JVM执行流程可以参考文章:JVM(Java虚拟机)详解 - 良月初十♧的博客 

    代码演示:

3.3 Bean 的初始化详解

  •  各种通知: 实现各种 Aware 方法,例如:BeanNameAware,BeanFactoryAware,ApplicationContextAware 的接口方法
  • 初始化前置方法:执行后 BeanPostProcessor 
  • 执行初始化方法: 1. @PostConstruct (依赖注入操作之后被执行)-->  注解方式                                                  2. init-method 方法(如果设置了才会执行)-->  init - method 方法 
  • 执行 BeanPostProcessor 初始化的后置方法

    总结:Bean 对象生命周期(从有到无的过程):

1、 开辟内存空间  (注:实例化 ≠ 初始化)
2、 设置属性 (上述两步都在实例化之前)

3、 初始化  

       3.1  各种通知(初始化了一部分内容就开始通知一下)

       3.2  初始化的前置方法

       3.2  初始化方法  (两种实现方法:xml方式,注解方式)

       3.3  初始化后置方法()

4、 使用 和 销毁 Bean 对象

 

代码演示 Bean 对象的 初始化步骤:

package com.java.demo.component;
import org.springframework.beans.factory.BeanNameAware;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class BeanComponent implements BeanNameAware {
    @Override
    public void setBeanName(String s) {
        System.out.println("执行了通知 BeanName -> " + s);
    }
    /*xml 初始化方法*/
    public void myInit() {
        System.out.println("XML 方式初始化 ");
    }
    @PostConstruct
    public void doPostConstruct() {
        System.out.println("注解的初始化方法");
    }

    public void sayHi() {
        System.out.println("执行 sayHi()");
    }

    @PreDestroy
    public void doPreDestroy() {
        System.out.println("do PreDestroy");
    }
}
//在启动类中
ClassPathXmlApplicationContext context = new
                ClassPathXmlApplicationContext("spring-config.xml");
        BeanComponent component = context.getBean("beanComponent",
                BeanComponent.class);
        component.sayHi();
        component.doPreDestroy();

    运行结果:

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

良月初十♧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值