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();
    }
}
  1. 启动Spring容器: 当代码执行到main方法中的ApplicationContext context = new ClassPathXmlApplicationContext("Spring-Config.xml");时, 启动Spring容器. 加载指定的配置文件.
  2. 初始化Bean: 初始化Bean, 就是根据配置文件中指定的要扫描的路径进行扫描, 查找五大类注解和@Bean方法注解. 如果扫描到有需要存到Spring中的Bean, 先实例化(分配内存空间), 然后进行初始化.
  3. 注册Bean对象到容器中. 这就是往Spring中存Bean对象.
  4. 将Bean装配到需要的类中, 这就是注入Bean对象.

Bean的生命周期

生命周期是一个对象从诞生到销毁的整个生命过程, 这个过程就叫做一个对象的生命周期.

Bean的生命周期分为以下五步:

1. 实例化Bean

为Bean分配内存空间


2. 设置属性

将当前Bean所依赖的Bean注入进来. 为当前Bean的初始化做准备.


3. Bean初始化

  1. 发送通知: 继承一些通知接口, 如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);
    }
}
  1. 执行初始化的前置方法: 通过实现 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;
    }
}
  1. 执行初始化方法: 可以在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");
    }
  1. 执行初始化的后置方法

这个方法在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();
    }
}
相关推荐
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭8 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
李小白669 小时前
Spring MVC(上)
java·spring·mvc
Lojarro11 小时前
【Spring】Spring框架之-AOP
java·mysql·spring
zjw_rp12 小时前
Spring-AOP
java·后端·spring·spring-aop
撒呼呼15 小时前
# 起步专用 - 哔哩哔哩全模块超还原设计!(内含接口文档、数据库设计)
数据库·spring boot·spring·mvc·springboot
天使day16 小时前
SpringMVC
java·spring·java-ee
壹佰大多17 小时前
【spring-cloud-gateway总结】
java·spring·gateway
CodeChampion17 小时前
60.基于SSM的个人网站的设计与实现(项目 + 论文)
java·vue.js·mysql·spring·elementui·node.js·mybatis
秋意钟17 小时前
Spring框架处理时间类型格式
java·后端·spring
科马21 小时前
【Redis】缓存
数据库·redis·spring·缓存