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中存入一个Bean, 并且要将这个Bean的作用域设置为原型作用域时, 需要用到@Scope("prototype")或者@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
在这里插入图片描述

@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容器.

解释: singleton 和 application 都表示在整个应用程序生命周期内只有一个实例, 区别在于 singleton 更常用于 Web 环境中,而 application 则更适合非 Web 环境. 选择使用哪种作用域取决于具体的应用程序架构和需求. 对于那些需要全局共享状态但不在 Web 环境中的情况, 例如非 Web 应用中的单例服务, 应使用application.

6. Http WebSocket作用域 websocket

在一个http webSocket的生命周期中定义一个Bean实例. 仅适用于Spring WebSocket中使用.
一个http webSocket的生命周期指的是从建立连接到关闭连接

Spring的执行流程和Bean的生命周期

Spring的执行流程

Bean的执行流程是从启动Spring容器 ->初始化Bean -> 将Bean注册到Spring中 -> 将Bean装配到需要的类中.


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. 实现这些接口的方法
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的属性进行修改或验证等.

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文件: 在这里插入图片描述
    <bean id="bean" class="com.annotation.controller.Bean" init-method="beanInit"
          destroy-method="destory"> </bean>
    public void beanInit(){
        System.out.println("通过xml初始化bean");
    }

(2) 通过注解@PostConstruct:

    @PostConstruct
    public void beanInit2(){
        System.out.println("通过注解初始化bean");
    }
  1. 执行初始化的后置方法

这个方法在Bean的初始化方法调用之后被调用. 和前置方法一样实现BeanPostProcessor接口, 重写后置方法postProcessAfterInitialization, 可以在Bean初始化之后进行一些自定义的操作. 例如, 对Bean进行额外的初始化或修改.

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

    public void print(){
        System.out.println("使用bean");
    }

5. 销毁Bean

销毁Bean的方法与初始化Bean的方法类似, 可以在xml中配置, 也可以通过注解. 与初始化bean不同的是, bean的初始化方法无需显式调用, 而bean的销毁方法需要显式调用.
(1) 通过xml配置

    <bean id="bean" class="com.annotation.controller.Bean" init-method="beanInit"
          destroy-method="destory"> </bean>
    public void destory(){
        System.out.println("通过xml销毁bean");
    }

(2) 通过注解@PreDestroy

    @PreDestroy
    public void destory2(){
        System.out.println("通过注解销毁bean");
    }

代码汇总:
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类:

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类:

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();
    }
}
  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

With Order @!147

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值