Bean作用域和生命周期
富春山居_ZYY 人气:0一、Bean 的作用域
在之前学习Java基础的时候,有接触到作用域这样的概念。一个变量并不一定在任何区域都是有效的,限定这个变量的可用性的代码范围就是该变量的作用域。
但是在这里 Bean 的作用域的概念和以前所认为的作用域有所不同。
Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式。
接下来,将会举一个案例来讲讲什么是作用域,什么是行为模式
案例概要:
创建一个共有的 Bean ,使用者A和使用者B都对该 Bean 进行了使用
使用者A在进行使用的时候,创建一个新的变量接收注入进来的 Bean,进行修改,将修改后的结果进行返回;
使用者B 直接将注入进来的 Bean 进行返回,不进行任何操作
代码实现:
步骤一:创建出一个公共的 Bean
@Component public class UserComponent { @Bean public User getUser() { User user = new User(); user.setId(1); user.setName("张三"); user.setPassWord("111111"); return user; } }
步骤二:使用者A获取到公共 Bean,进行修改
@Controller public class UserControllerA { @Autowired private User user; public User getUser1() { System.out.println("使用者A拿到的原始user:" + user); User myUser = user; myUser.setName("李四"); return myUser; } }
步骤三:使用者B直接将获取到的公共的 Bean 进行返回
@Controller public class UserControllerB { @Autowired private User user; public User getUser2() { return user; } }
步骤四:main 中获取 UserControllerA 类和 UserControllerB 类使用查看
public class Start { public static void main(String[] args) { //获取 Spring 上下文 ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); //获取到Spring容器中的 UserControllerA 类(Bean 对象) UserControllerA userControllerA = context.getBean("userControllerA",UserControllerA.class); //使用 Bean 对象 System.out.println("使用者A->"+userControllerA.getUser1()); //获取到Spring容器中的 UserControllerA 类(Bean 对象) UserControllerB userControllerB = context.getBean("userControllerB",UserControllerB.class); //使用 Bean 对象 System.out.println("使用者B->"+userControllerB.getUser2()); } }
预期结果:
使用者 A修改后,其结果是修改过的;使用者 B 没有修改过,其结果应该和原始user一样
结果显示:
和预期结果有所不同,使用者 A 和使用者 B 所得到的结果都是被修改过的。
这是因为在 Spring 中,Bean 默认情况下是单例状态,大家用的都是同一份对象,是全局共享的,当有其他人修改了该对象,另一个人所获取到的对象就是被修改过的,这便是 Bean 六大作用域之一——单例作用域(singleton)
在写 WEB 项目的时候,我们知道 DataSource 就是单例模式,使用单例,好处多多,可以确保所有对象都访问唯一实例,而且减少了内存的开支和系统性能的开销,因此 Bean 默认情况下是单例状态。
若想要按照预期结果输出,就需要将 Bean 的作用域设置成原型作用域,即无论谁来使用 Bean 对象,获取到的都是原始的 Bean 对象,大家各玩各的。
需要在注入的对象上使用注解修改作用域,有以下两种方法(Scope就是作用域的意思)
- 直接将作用域以 String 类型写到()中
- 使用全局参数,类似于枚举
@Component public class UserComponent { //@Scope("prototype") //方法一 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) //方法二 @Bean public User getUser() { User user = new User(); user.setId(1); user.setName("张三"); user.setPassWord("111111"); return user; } }
Bean 的 6 种作用域
singleton:单例作用域
- Bean 在 IoC 容器中只存在一个实例
- 当 Bean 对象属性状态无需更新时使用该作用域
- Spring 支持
prototype:原型作⽤域/多例作⽤域
- 每次获取该 Bean 时都会创建新的实例,即获取的都是原始的 Bean
- 当 Bean 对象属性状态会更新时使用该作用域
- Spring 支持
request:请求作用域
- 每次 HTTP 请求都会创建新的 Bean 实例
- 一次 HTTP 请求和响应共享的 Bean
- 限定 SpringMVC
session:会话作用域
- 在一个 HTTP session中,创建一个 Bean 实例
- 用户会话共享一个 Bean
- 限定 SpringMVC
application:全局作用域
- 在一个 HTTP Servlet Context中,创建一个 Bean 实例
- 一个 WEB 上下文中共享一个 Bean
- 限定 SpringMVC
websocket: HTTP WebSocket 作⽤域(不常用)
- 在一个 HTTP WebSocket 中,创建一个 Bean 实例
- 限定 Spring WebSocket
单例作用域和全局作用域比较像,但全局作用域范围没有单例作用域大,前者是 Spring 核心的作用域,后者是 Spring Web 中的作用域,前者作用于 IoC 容器,后者作用于 Servlet 容器
二、Spring 的执行流程
Spring 的执行流程也可以说是 Bean的执行流程,主要分成4部分
- 启动 Spring 容器
- 加载 XML 文件,实例化 Bean(进行内存的分配)
- Bean 存到 Spring 容器中(五大类注解,方法注解)
- 将存储的 Bean 中的注入的对象属性进行初始化,即进行装配(取出 Bean)
三、Bean 的生命周期
Bean 的生命周期即 Bean 从诞生到销毁的整个过程
实例化 Bean 对象,申请内存空间
设置 Bean 的属性,进行依赖注入和装配
Bean 的初始化
- 各种 Aware 感知:BeanNameAware、BeanFactoryAware、ApplicationContextAware、…
- 执⾏ BeanPostProcessor 初始化前置⽅法
- 初始化方法:构造器方法
@PostConstructor
(对象加载完依赖注入后执行) - 初始化方法:
init-method
- 执⾏ BeanPostProcessor 初始化后置⽅法
使用 Bean
销毁 Bean
- 销毁方法:
@PreDestroy
- 接口方法:
DisposableBean
- 销毁方法:
destroy-method
补充
实例化和初始化的区别
- 实例化:这里的实例化和从前的实例化对象是有区别的,这里的实例化就负责内存的分配,一个从无到有的过程。实例化和属性设置时 Java 级别的系统“事件”,操作过程是不可人工干预的
- 初始化:是对 Bean 进行填充的过程,程序员可以进行介入,真正的将 Bean 放到 Spring 容器中
@PostConstructor 和 @PreDestroy 是注解方式进行初始化和注销,init-method 和 destroy-method 是 XML 方法进行初始化和注销,一般只要使用其中的一种进行初始化
设置属性一定要在初始化之前,因为初始化也可能需要使用到注入的对象,如果没有进行属性的设置,初始化就会出现问题
案例:生命周期演示
@Component public class BeanLife implements BeanNameAware { @Override public void setBeanName(String s) { System.out.println("BeanName 感知:"+ s); } @PostConstruct public void postConstructor() { System.out.println("执行初始化方法:PostConstructor"); } @PreDestroy public void preDestroy() { System.out.println("执行销毁方法:PreDestroy"); } public void initMethod() { System.out.println("执行初始化方法:init-method"); } public void destroyMethod() { System.out.println("执行销毁方法:destroy-method"); } }
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.bit.beans"></content:component-scan> <beans> <bean id="beanLife" class="com.bit.beans.Component.BeanLife" init-method="initMethod" destroy-method="destroyMethod"></bean> </beans> </beans>
调用
public class Start { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); BeanLife beanLife = context.getBean("beanLife",BeanLife.class); System.out.println("---使用 Bean 对象---"); System.out.println("---注销 Bean 对象--- "); context.destroy();//容器的销毁相当于销毁所有的 Bean } }
结果显示:
流程图展示:
加载全部内容