当前位置: 首页 > news >正文

Bean 的作用域和生命周期

Bean 的作用域和生命周期

  • Bean 的作用域
    • 案例
    • Bean 的六种作用域
    • Bean 的多例模式
      • 直接通过 prototype
      • 通过 ConfigurableBeanFactory
  • Spring 的执行流程
  • Bean 的生命周期
    • 生命周期代码演示

Bean 的作用域

在使用 Bean 的时候,一个公共的 Bean,交给 A用户 和 B用户 来使用,如果 A用户 偷偷修改了 Bean 的数据,那么 B用户 拿到的数据和预期的就不一样了。

案例

先在 Spring 当中存储一个 User 对象:

@Component
public class Users {
    @Bean
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
}

A用户 拿到对象,并作出修改:

@Component
public class BeanScope1 {
    @Autowired
    private User user1;
    public User getUser() {
        User user = user1;
        user.setName("李四");
        return user;
    }
}

把张三修改成了李四,再获取对象进行输出的时候:

@Component
public class BeanScope1 {
    @Autowired
    private User user1;
    public User getUser() {
        User user = user1;
        user.setName("李四");
        return user;
    }
}

运行结果如下:
在这里插入图片描述
产生这样的原因是因为 Bean 在 Spring 中,默认情况下是单例状态,也就是所有人的使用都是同一个对像。这样可以很好的节约资源,避免资源的浪费。

作用域就是:Bean 在 Spring 中只有一份,它是全局共享的,那么当其他人修改了这个值之后,另外一个人读取到的就是被修改的值。

Bean 的六种作用域

  1. singleton:单例作用域(默认)在这种模式下的 Bean 在 IoC 容器里面,只存在一个实例。
  2. prototype:原型作用域(多例模式)每次对作用域下的 Bean 请求,都会创建新的实例,然后再去获取 Bean。
  3. request:请求作用域(Spring MVC)每次 Http 请求会创建新的 Bean 实例,类似于 prototype。
  4. session:会话作用域(Spring MVC)在一个 http session 中,定义一个 Bean 实例。记录用户的登录信息。
  5. application:全局作用域(Spring MVC)更多人使用的时候,就用 application。也是单例模式,应用在 Web 应用的上下文信息。
  6. websocket:Http WebSocket 作用域(Spring WebSocket)在 WebSocket 会话中使用。

设置作用域的时候,只需要通过 @Scope 注解就可以了

  1. 直接设置值:@Scope(“prototype”)
  2. 使用枚举设置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

默认情况下的单例作用域

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        BeanScope1 beanScope1 = context.getBean(BeanScope1.class);
        User user1 = beanScope1.getUser();
        System.out.println("BeanScope1:" + user1);

        BeanScope2 beanScope2 = context.getBean(BeanScope2.class);
        User user2 = beanScope2.getUser();
        System.out.println("BeanScope2:" + user2);
    }
}

在这里插入图片描述
可以发现 User 对象全被改了,防止被改的话,就在存 User 对象之前,给它设置 @Scope

Bean 的多例模式

也就是为了防止出现像上面这种情况,在使用的时候已经被别人修改。

直接通过 prototype

@Component
public class Users {
    @Bean(name = "user1")
    @Scope("prototype")
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
}

代码如下:

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        BeanScope1 beanScope1 = context.getBean(BeanScope1.class);
        User user1 = beanScope1.getUser();
        System.out.println("BeanScope1:" + user1);

        BeanScope2 beanScope2 = context.getBean(BeanScope2.class);
        User user2 = beanScope2.getUser();
        System.out.println("BeanScope2:" + user2);
    }
}

运行结果如下:
在这里插入图片描述
就是通过 prototype 对对象设置,每次一个请求就生成一个对象。

通过 ConfigurableBeanFactory

也就是设置 @Scope 的 ConfigurableBeanFactory.SCOPE_PROTOTYPE 来保证多例模式

@Component
public class Users {
    @Bean(name = "user1")
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
}

运行结果如下:
在这里插入图片描述
仍然可以保证多例模式。

Spring 的执行流程

  1. Spring 项目在运行的时候,会先启动容器,也就是我们在 main 方法里面写了这样的代码之后:

    在这里插入图片描述
    在这里启动容器之后,就要加载配置文件:beans.xml 了。

  2. 然后接下来就是根据配置完成 Bean 的初始化:

    在这里插入图片描述
    会通过这里的扫描路径,来存入 Bean 。将指定路径种带有五大类注解的普通类存入 Spring 当中,还有类里面带有方法注解的方法,其返回对象也存入 Spring 当中。

  3. 注册 Bean 对象到容器中,只有在包扫描的路径上的类,且使用 Spring 的注解才可以被注册到容器当中:
    在这里插入图片描述

  4. 装配 Bean 属性,也就是把 Bean 注册到其他类当中:
    在这里插入图片描述

总的来说:先去启动容器,加载 xml 配置文件。然后,扫描五大类注解,随后,将具有五大类注解的类,存入 Spring 当中。如果 存入的过程中,存在属性的注入,就先执行属性的注入。然后,再继续执行 类 的 实例化。实例化之后,将其存入Spring 中。

Bean 的生命周期

生命周期就是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命周期。Bean 的生命周期有以下几步:

  1. 实例化 Bean(为 Bean 分配内存空间)
  2. 设置属性(Bean 注入和装配)
  3. Bean 初始化
    a)执行各种通知(各种Aware)如:BeanNameAware,BeanFactoryAware,ApplicationContextAware 的接口方法。
    b)执行 BeanPostProcessor 初始化前置方法。
    c)执行 @PostConstruct 初始化方法,依赖注入操作之后被执行。
    d)执行自己指定的 init-method 方法(如果有指定的话
    e)执行 BeanPostProcessor 初始化后置方法。
  4. 使用 Bean
  5. 销毁 Bean,通过方法来销毁容器:如 @PreDestroy、DisposableBean、destroy-method

执行流程图
在这里插入图片描述

生命周期代码演示

代码如下:

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class BeanLifeComponent implements BeanNameAware {
    @PostConstruct
    public void postConstruct() {
        System.out.println("执⾏ PostConstruct()");
    }
    public void init() {
        System.out.println("执⾏ BeanLifeComponent init-method");
    }
    @PreDestroy
    public void preDestroy() {
        System.out.println("执⾏:preDestroy()");
    }
    public void setBeanName(String s) {
        System.out.println("执⾏了 setBeanName ⽅法:" + s);
    }
}

XML 配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:content="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
<content:component-scan base-package="com.component">
</content:component-scan>
<beans>
<bean id="beanLifeComponent"
class="com.component.BeanLifeComponent" init-method="init"></bean>
</beans>
</beans>

启动类:

import com.controller.BeanLife;
import 
org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanLifeTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring-config.xml");
        BeanLife life = context.getBean(BeanLife.class);
        System.out.println("执⾏ main ⽅法");
// 执⾏销毁⽅法
        context.destroy();
    }
}

运行结果如下:
在这里插入图片描述
造成两次通知,是因为 BeanLifeComponent 有注解,原先代码又配置了扫描路径,所以还会加载一次。

现设置属性,再初始化,是为了防止还没有注入的时候,就初始化,然后空指针异常。

相关文章:

  • 【博客484】alertmanager-----告警处理源码剖析
  • 第7章 - 多无人机系统的协同控制 --> 无人机模型分析
  • [Unity独立/合作开发]实现背包系统中物品的拾取拖拽掉落还有换位置
  • 【数据结构】经典八大排序算法(万字大总结+动图)
  • 使用vue-cli搭建SPA项目
  • Windows搭建hexo教程
  • 多线程案例
  • 服务器使用Nginx部署Vue项目
  • 尚医通(二)
  • Java实现登录功能(一)
  • MySQL-插入、更新与删除数据
  • 秋招每日一题T31——二叉搜索子树的最大键值和
  • 天龙八部科举答题问题和答案(全7/8)
  • 【操作系统】内存管理(二)—— 程序运行的基本过程和要求
  • 猿创征文丨赶紧进来!!! 详细介绍C语言指针(九千字完结篇)
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • bootstrap创建登录注册页面
  • JS题目及答案整理
  • PAT A1050
  • WebSocket使用
  • WePY 在小程序性能调优上做出的探究
  • windows下如何用phpstorm同步测试服务器
  • 动态规划入门(以爬楼梯为例)
  • 多线程 start 和 run 方法到底有什么区别?
  • 飞驰在Mesos的涡轮引擎上
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 前端面试题总结
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 微服务核心架构梳理
  • 小程序开发中的那些坑
  • 一个项目push到多个远程Git仓库
  • 赢得Docker挑战最佳实践
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • 扩展资源服务器解决oauth2 性能瓶颈
  • # C++之functional库用法整理
  • #100天计划# 2013年9月29日
  • #etcd#安装时出错
  • #我与Java虚拟机的故事#连载17:我的Java技术水平有了一个本质的提升
  • (C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。
  • (Forward) Music Player: From UI Proposal to Code
  • (poj1.3.2)1791(构造法模拟)
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (附程序)AD采集中的10种经典软件滤波程序优缺点分析
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (六)软件测试分工
  • (免费分享)基于springboot,vue疗养中心管理系统
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (三) prometheus + grafana + alertmanager 配置Redis监控
  • (小白学Java)Java简介和基本配置
  • (一)WLAN定义和基本架构转
  • (转)winform之ListView
  • (转载)Google Chrome调试JS
  • ***检测工具之RKHunter AIDE
  • **PyTorch月学习计划 - 第一周;第6-7天: 自动梯度(Autograd)**