Bean 作用域与生命周期
对于 Spring 来说,核心操作对象就是存和取 Bean ,接下来就 Bean 的作用域与生命周期进行探讨。
文章目录
- Bean 作用域与生命周期
- 一、作用域的定义
- 1.1、Bean 的6种作用域
- 1.2、Bean作用域设置方法
- 二、Bean 的生命周期
- 2.1、Bean 的执行流程
- 2.2、Bean 的生命周期分为5大部分 ☆
通过例子看作用域:
1、先在Spring IoC 容器中存放一个 Bean(公共 Bean);
@Component
public class StudentComponent {@Beanpublic Student studentNum1() {Student student = new Student();student.setId(1);student.setName("java"); //student.setPassword("123");return student;}
}
这段代码表示在 Spring IoC 容器中已经存上了一个 Bean 对象 并且 对象的 id 设置成 1,对象的 name 设置成了 java;对象的 password 设置成了 123.
2、A用户来取出 Spring IoC 容器中的这个 Bean 对像进行了修改操作;
@Controller
public class StuedntControllerA {@Autowiredprivate Student student;public Student getStudent() {System.out.println("修改 student 信息");student.setPassword("456");student.setName("python");student.setId(2);return student;}
}
这段代码表示A用户取出容器中的 Bean 对象之后又再次基础上修改了 Bean对象里面的信息,password 改成为456,name改成了 python。
3、B用户什么也不操作只是在 Spring IoC 容器中取出 Bean 对象。
@Controller
public class StudentControllerB {@Resourceprivate Student student;public Student getStudent() {return student;}
}
平常我们会认为 A 用户在 Spring IoC 容器中里面取出的对象做出了修改,可以修改成功,变成修改之后的样子,而 B 用户他没有任何操作只是从 Spring IoC 里面取出了 Bean 对象,故里面的数据还是原来 Bean 对象的数据。
但是我们还是要运行程序看结果:
最后的结果:是 A 用户确实修改了信息,但是 B 用户的信息取得是 A 用户的信息。
原因分析:
因为在默认情况下 Bean 是单例模式(singleton),也就是所有人使用的都是同一个对象,使用单例模式好处之一就是可以很大程度的提高性能 ,所以在 Spring 中 Bean 的作用域默认是 singleton 单例模式。
都是同一个对象 Bean。而 Bean 的作⽤域是指 Bean 在 Spring 整个框架中的某种行为模式,比如 上面singleton 单例作⽤域,就表 示 Bean 在整个 Spring 中只有⼀份,它是全局共享的,那么当其他⼈修改了这个值之后,那么另⼀个 ⼈读取到的就是被修改的值。
一、作用域的定义
限定程序中变量的可用范围叫做作用域,或者说在源代码中定义变量的某个区域就叫做作用域。
1.1、Bean 的6种作用域
Spring 容器在初始化一个 Bean 的实例时,同时会指定该实例的作用域。
- singleton:单例作用域(Spring 支持)
- prototype:原型作用域(多例作用域)(Spring 支持)
- request:请求作用域(Spring MVC 生效)
- session:会话作用域(Spring MVC 生效)
- application:全局作用域(Spring MVC 生效)
- websocket:Http WebSocket 作用域(Spring MVC 生效)了解
这篇的后面四个都是用在 Spring MVC 里面的,Spring MVC 在 Spring 中也是一个非常重要的框架,后面也会了解到,这篇主要看看 Spring 支持的,其中 singleton 已经了解到了,接下来就是 prototype。
prototype 描述:每次对该作用域下的 Bean 请求都会创建新的实例:获取 Bean(getBean方法)及装配 Bean (通过@Autowired注入)都是新的对象实例。
所以说适用上面的情况,创建出新的实例,不会影响 A 用户修改了Bean 之后,B 用户再去拿,就是原先 Bean 对象里面的数据不会改变。
1.2、Bean作用域设置方法
使用 @Scope
标签 可以用来声明 Bean 的作用域,比如设置 Bean 的作用域。
1、直接写 String 类行的作用域
@Scope("prototype")
2、使用全局的参数
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
代码如下:
@Component
public class StudentComponent {
// @Scope("prototype") 方式一@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // 方式二@Beanpublic Student studentNum1() {Student student = new Student();student.setId(1);student.setName("java");student.setPassword("123");return student;}
}
这样使用多列模式 prototype
B 用户就不会受到影响
二、Bean 的生命周期
2.1、Bean 的执行流程
Bean 的执行流程(Spring 的执行流程):启动 Spring 容器 ——> 实例化 Bean (创建 Bean ,分配内存空间)——> Bean 注册到 Spring 中(存)——> 将 Bean 装配到需要的类中(取)。
Spring Core 执行流程:
1、启动 Spring 容器(main方法)
2、加载 XML,实例化并申请内存
3、将添加了5大注解的对象存储到容器中。
4、将存储的 Bean 中的注入的对象属性进行初始化。
2.2、Bean 的生命周期分为5大部分 ☆
Bean 的生命周期(从 Bean 诞生到销毁的整个过程)(☆☆☆☆☆)
1、实例化 Bean 对象,只是申请内存空间;
2、先设置 Bean 属性 (依赖注入和+装配)
3、再对 Bean 初始化
- 各种 Aware 感知:BeanNameAware、BeanFactoryAware…的接口方法
- 初始化前置方法(BeanPostProcessor)
- 构造器方法 @PostConstruct 初始化方法 (依赖注入操作之后被执行)☆
- init-method 初始化方法 (xml)
- 初始化后置方法(BeanPostProcessor)
实力化 和 初始化的区别
实例化和属性设置是 Java 级别的系统“事件”,其操作过程不可人工干预和修改;而初始化是给 开发者提供的,可以在实例化之后,类加载完成之前进⾏⾃定义“事件”处理。
4、使用 Bean
5、销毁 Bean
- @PreDestroy
- destroy-method
再对 Bean 初始化里面的东西进行解释:
1、Aware 这个就是一个感知通知,映射关系,比如说在方法注解@Bean(“pwd”)上面使用了别名 Aware需要找对应关系通知映射
2、为什么设置 Bean 属性在初始化前面,因为只有被类注入,初始化的时候不会因为需要类的其他属性报错。
3、初始化方法 @PostConstruct 和 init-method 本质上上一样的,是不同时期的产物,程序员@PostConstruct 可以根据业务配置初始化条件,属于是注解Spring 时期的;init-method 是 xml 时期的,比较远古。
4、这也对应了销毁 Bean 里面的@PreDestroy和destroy-method
生命周期演示代码
@Component
public class BeanLife implements BeanNameAware {@Overridepublic void setBeanName(String s) {System.out.println("执行:BeanNameAware setBeanName 方法:"+s);}// 初始化方式一:@PostConstruct@PostConstructpublic void postConstruct() {System.out.println("执行了:@PostConstruct");}// 初始化方式二:init-methodpublic void init(){System.out.println("执行了:init-method"); //要是用 xml 配置 <Beans>里面的标签}// 销毁方法一:@PreDestroypublic void preDestroy(){System.out.println("执行了:preDestroy");}//销毁方式二:public void myDestroy(){System.out.println("执行了:myDestroy"); //要是用 xml 配置 <Beans>里面的标签}}
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"><!-- 配置一下:bean注解扫描的根路径(方面后面更简单存储对象到spring容器)--><content:component-scan base-package="com.yuanye.beans"></content:component-scan><beans><bean id="beanLife" class="com.yuanye.beans.Component.BeanLife" init-method="init"destroy-method="myDestroy"></bean></beans>
</beans>
特别注意 < Beans > 里面的配置
调用类方法:
public class App {public static void main(String[] args) {// 是ApplicationContext的子类,获得 spring 上下文对象ClassPathXmlApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");BeanLife beanLife = context.getBean("beanLife",BeanLife.class);System.out.println("使用 Bean");System.out.println("销毁 Bean");context.destroy();}
}
流程图如下:
总结:
本篇文章介绍了 Bean 的6种作用域和 Bean 的执行流程,还有重要的 Bean 的生命周期,生命周期里面的实例化和初始化是不一样的,一个是不能人控制,一个是可以人为控制;Bean 的生命周期里面特别是初始化环节很重要。