小王学习录
- 前言
- Bean的作用域
-
- 什么是Bean的作用域
- Bean的六种作用域
-
- [1. 单例作用域 singleton](#1. 单例作用域 singleton)
- [2. 原型作用域 prototype (多例作用域)](#2. 原型作用域 prototype (多例作用域))
- [3. 请求作用域 request](#3. 请求作用域 request)
- [4. 会话作用域 session](#4. 会话作用域 session)
- [5. 全局作用域 application](#5. 全局作用域 application)
- [6. Http WebSocket作用域 websocket](#6. Http WebSocket作用域 websocket)
- Spring的执行流程和Bean的生命周期
-
- Spring的执行流程
- Bean的生命周期
-
- [1. 实例化Bean](#1. 实例化Bean)
- [2. 设置属性](#2. 设置属性)
- [3. Bean初始化](#3. Bean初始化)
- [4. 使用Bean](#4. 使用Bean)
- [5. 销毁Bean](#5. 销毁Bean)
前言
在之前的文章中我们知道, Spring是一个存放Bean的IoC容器, 这个容器的两大核心功能是存Bean和取Bean. 今天来详细了解一下Bean的作用域和生命周期.
Bean的作用域
什么是Bean的作用域
以前我们接触到的作用域说的是变量的可作用范围. 而今天要讲到Bean的作用域不同于之前的作用域, Bean的作用域说的是一个Bean在整个Spring框架中的行为模式. 行为模式有六种. 也就是作用域有六种. 下面将具体介绍这六种行为模式(作用域).
Bean的六种作用域
1. 单例作用域 singleton
singleton单例作用域, 顾名思义. 单例作用域就是在Spring框架中只存有该Bean的一个实例. 在整个框架中对该Bean的注入操作都是同一个对象.
这种作用域是Spring默认的Bean作用域, 适合于无状态的Bean. 所谓无状态就是该Bean的属性对象不需要更新.
当向Spring中存入一个Bean时, Spring默认这个Bean的作用域就是单例的, 因此不需要另外显式配置.
2. 原型作用域 prototype (多例作用域)
prototype多例作用域, 在每次获取Bean(ApplicationContext或通过注解进行注入)时, 都会获取到一个新的实例对象. 该实例对象遵循Spring框架对其进行的初始化操作(@Bean进行初始化).
实际上, 在每次获取实例对象时, 都是通过拷贝一份已有的原型对象实现的.
这种作用域适用于有状态的Bean. 需要注意的是, 这种作用域只适用于Spring框架, 对于Spring MVC和Spring Boot来说是不支持的.
当向Spring中存入一个Bean, 并且要将这个Bean的作用域设置为原型作用域时, 需要用到@Scope("prototype")
或者@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
java
@Service
public class UserService {
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean
public User setUser(){
User user = new User();
user.setUserid(2);
user.setUsername("张三");
user.setPassword("123");
return user;
}
}
3. 请求作用域 request
在每此http请求时会生成一个新的实例. 本此请求和响应公用一个实例. 适用于需要在同一次请求处理期间共享数据的Bean, 确保每个请求都有自己的实例. 在Spring MVC和Spring Boot中使用, Spring中不能使用.
使用时需要用到@RequestScope
4. 会话作用域 session
每个Http会话会创建一个实例, 该实例在整个会话中有效. 适用于在整个会话期间内共享数据的Bean. 在Spring MVC中使用, 在Spring中不能使用.
使用时需要用到@SessionScope
注解.
5. 全局作用域 application
在整个web应用程序中共享一个Bean, 和单例模式相似但不同.
不同之处在于全局作用域是在整个web应用程序中共享一个Bean, 他是Spring Web中的作用域, 作用于Servlet容器.
而单例模式是在整个Spring容器中共享一个Bean. 是Spring中的作用域, 作用于IoC容器.
6. Http WebSocket作用域 websocket
在一个http webSocket的生命周期中定义一个Bean实例. 仅适用于Spring WebSocket中使用.
Spring的执行流程和Bean的生命周期
Spring的执行流程
Bean的执行流程是从启动Spring容器 ->初始化Bean -> 将Bean注册到Spring中 -> 将Bean装配到需要的类中.
java
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Spring-Config.xml");
UserController controller1 = context.getBean("userController", UserController.class);
controller1.setUser();
UserController2 controller2 = context.getBean("userController2", UserController2.class);
controller2.print();
}
}
- 启动Spring容器: 当代码执行到main方法中的
ApplicationContext context = new ClassPathXmlApplicationContext("Spring-Config.xml");
时, 启动Spring容器. 加载指定的配置文件. - 初始化Bean: 初始化Bean, 就是根据配置文件中指定的要扫描的路径进行扫描, 查找五大类注解和@Bean方法注解. 如果扫描到有需要存到Spring中的Bean, 先实例化(分配内存空间), 然后进行初始化.
- 注册Bean对象到容器中. 这就是往Spring中存Bean对象.
- 将Bean装配到需要的类中, 这就是注入Bean对象.
Bean的生命周期
生命周期是一个对象从诞生到销毁的整个生命过程, 这个过程就叫做一个对象的生命周期.
Bean的生命周期分为以下五步:
1. 实例化Bean
为Bean分配内存空间
2. 设置属性
将当前Bean所依赖的Bean注入进来. 为当前Bean的初始化做准备.
3. Bean初始化
- 发送通知: 继承一些通知接口, 如BeanNameAware, BeanFactoryAware, ApplicationContextAware. 实现这些接口的方法
java
public class Bean implements BeanNameAware, BeanFactoryAware {
@Override
public void setBeanName(String s) {
System.out.println("通知bean名为 -> " + s);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("通知工厂bean为 -> " + beanFactory);
}
}
- 执行初始化的前置方法: 通过实现
BeanPostProcessor
接口, 重写postProcessBeforeInitialization
方法. 前置方法和后置方法都不用显式调用
这个方法在Bean的初始化方法调用之前被调用. 实现BeanPostProcessor
接口并覆写该方法, 可以在Bean初始化之前进行一些自定义的操作. 例如, 对Bean的属性进行修改或验证等.
java
public class Bean implements BeanNameAware, BeanFactoryAware , BeanPostProcessor {
@Override
public void setBeanName(String s) {
System.out.println("通知bean名为 -> " + s);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("通知工厂bean为 -> " + beanFactory);
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("执行初始化前置方法");
return bean;
}
}
- 执行初始化方法: 可以在xml文件中配置, 或者也可以通过注解@PostConstruct. 两者都出现时, 通过注解的方式优先级更高.
(1) 通过xml文件:
xml
<bean id="bean" class="com.annotation.controller.Bean" init-method="beanInit"
destroy-method="destory"> </bean>
java
public void beanInit(){
System.out.println("通过xml初始化bean");
}
(2) 通过注解@PostConstruct:
java
@PostConstruct
public void beanInit2(){
System.out.println("通过注解初始化bean");
}
- 执行初始化的后置方法
这个方法在Bean的初始化方法调用之后被调用. 和前置方法一样实现BeanPostProcessor
接口, 重写后置方法postProcessAfterInitialization
, 可以在Bean初始化之后进行一些自定义的操作. 例如, 对Bean进行额外的初始化或修改.
java
public class Bean implements BeanNameAware, BeanFactoryAware , BeanPostProcessor {
@Override
public void setBeanName(String s) {
System.out.println("通知bean名为 -> " + s);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("通知工厂bean为 -> " + beanFactory);
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("执行初始化前置方法");
return bean;
}
public void beanInit(){
System.out.println("通过xml初始化bean");
}
@PostConstruct
public void beanInit2(){
System.out.println("通过注解初始化bean");
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("执行初始化后置方法");
return bean;
}
}
4. 使用Bean
java
public void print(){
System.out.println("使用bean");
}
5. 销毁Bean
销毁Bean的方法与初始化Bean的方法类似, 可以在xml中配置, 也可以通过注解. 与初始化bean不同的是, bean的初始化方法无需显式调用, 而bean的销毁方法需要显式调用.
(1) 通过xml配置
xml
<bean id="bean" class="com.annotation.controller.Bean" init-method="beanInit"
destroy-method="destory"> </bean>
java
public void destory(){
System.out.println("通过xml销毁bean");
}
(2) 通过注解@PreDestroy
java
@PreDestroy
public void destory2(){
System.out.println("通过注解销毁bean");
}
代码汇总:
xml配置:
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:context="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">
<context:component-scan base-package="com.annotation"></context:component-scan>
<bean id="bean" class="com.annotation.controller.Bean" init-method="beanInit"
destroy-method="destory" scope="prototype"> </bean>
</beans>
Bean类:
java
public class Bean implements BeanNameAware, BeanFactoryAware, BeanPostProcessor{
@Override
public void setBeanName(String s) {
System.out.println("通知bean名为 -> " + s);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("通知工厂bean为 -> " + beanFactory);
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("执行初始化前置方法");
return bean;
}
public void beanInit(){
System.out.println("通过xml初始化bean");
}
@PostConstruct
public void beanInit2(){
System.out.println("通过注解初始化bean");
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("执行初始化后置方法");
return bean;
}
public void destory(){
System.out.println("通过xml销毁bean");
}
@PreDestroy
public void destory2(){
System.out.println("通过注解销毁bean");
}
public void print(){
System.out.println("使用bean");
}
}
Main类:
java
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Spring-Config.xml");
// UserController controller1 = context.getBean("userController", UserController.class);
// controller1.setUser();
// UserController2 controller2 = context.getBean("userController2", UserController2.class);
// controller2.print();
Bean bean = context.getBean("bean", Bean.class);
bean.print();
bean.destory();
bean.destory2();
}
}