• Spring框架(四)Spring的Bean作用域和生命周期


    一,作用域定义

    限定程序中变量的可用范围叫做作用域,而 Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式,比如 singleton 单例作用域,就表示 Bean 在整个 Spring 中只有⼀份,它是全局共享的,那么当其他⼈修改了这个值之后,那么另⼀个⼈读取到的就是被修改的值

    二,同⼀类型多个 @Bean 报错的解决办法

    • 方法1:使用正确的bean name 获取
    • 方法2:使用@Resource设置name属性
    // 注⼊
    @Resource(name = "user1")
    private User user;
    
    • 1
    • 2
    • 3
    • 方法3:使用@Autowired+@Qualifier
    // 注⼊
    @Autowired
    @Qualifier(value = "user2")
    private User user;
    
    • 1
    • 2
    • 3
    • 4

    三,Bean的6种作用域

    Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作用域。Spring有 6 种作用域,最后四种是基于 Spring MVC 生效的:

    1.singleton:单例作用域

    • 描述:该作用域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过
      applicationContext.getBean等方法获取)及装配Bean(即通过@Autowired注入)都是同⼀
      个对象。
    • 场景:通常无状态的Bean使用该作用域,无状态表示Bean对象的属性状态不需要更新
    • 备注:Spring默认选择该作用域

    2.prototype:原型作用域(多例作用域)

    • 描述:每次对该作⽤域下的Bean的请求都会创建新的实例:获取Bean(即通过
    • applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是新的
      对象实例。
    • 场景:通常有状态的Bean使⽤该作⽤

    3.request:请求作用域

    • 描述:每次http请求会创建新的Bean实例,类似于prototype
    • 场景:⼀次http的请求和响应的共享Bean
      备注:限定SpringMVC中使⽤

    4.session:回话作用域

    • 描述:在⼀个http session中,定义⼀个Bean实例
    • 场景:⽤户回话的共享Bean, ⽐如:记录⼀个⽤户的登陆信息
    • 备注:限定SpringMVC中使⽤

    5.application:全局作用域

    • 描述:在⼀个http servlet Context中,定义⼀个Bean实例
    • 场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息
    • 备注:限定SpringMVC中使⽤

    6.websocket:HTTP WebSocket 作用域

    • 描述:在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bean实例
    • 场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息头。第⼀次初始化后,直到WebSocket结束都是同⼀Bean。
    • 备注:限定Spring WebSocket中使⽤

    7,单例作用域(singleton)和全局作用域(application)区别

    • singleton 是 Spring Core 的作用域;application 是 Spring Web 中的作用域;
    • singleton 作用于 IoC 的容器,而application 作用于 Servlet 容器

    四,Spring框架中的单例bean是线程安全的吗

    • 不是,Spring框架中的单例bean不是线程安全的
    • spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。
      实际上大部分时候 spring bean 无状态的(比如 dao 类),所以某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。
      ①有状态就是有数据存储功能。
      ②无状态就是不会保存数据。

    五,Spring如何处理线程并发问题

    • 在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题
    • ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式
    • ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal

    六,Spring框架中bean的生命周期

    • Bean 执行流程(Spring 执行流程):启动 Spring 容器 -> 实例化 Bean(分配内存空间,从⽆到有)-> Bean 注册到 Spring 中(存操作) -> 将 Bean 装配到需要的类中(取操作)
    • Bean的生命周期:
      1.实例化 Bean(为 Bean 分配内存空间)
      2.设置属性(Bean 注⼊和装配)
      3.Bean 初始化
      实现了各种 Aware 通知的⽅法,如 BeanNameAware、BeanFactoryAware、
      ApplicationContextAware 的接⼝⽅法;
      执⾏ BeanPostProcessor 初始化前置⽅法;
      执⾏ @PostConstruct 初始化⽅法,依赖注⼊操作之后被执⾏;
      执⾏⾃⼰指定的 init-method ⽅法(如果有指定的话);
      执⾏ BeanPostProcessor 初始化后置⽅法。
      4.使用Bean
      5.销毁 Bean
      销毁容器的各种⽅法,如 @PreDestroy、DisposableBean 接⼝⽅法、destroy-method
    • 具体实例:
      ean 的⽣命流程看似繁琐,但咱们可以以⽣活中的场景来理解它,⽐如我们现在需要买⼀栋房⼦,那
      么我们的流程是这样的:
      1.先买房(实例化,从⽆到有);
      2.装修(设置属性);
      3.买家电,如洗⾐机、冰箱、电视、空调等([各种]初始化);
      4.⼊住(使⽤ Bean);
      5.卖出去(Bean 销毁)

    七,Bean生命周期演示

    import org.springframework.beans.factory.BeanNameAware;
            import org.springframework.stereotype.Component;
            import javax.annotation.PostConstruct;
            import javax.annotation.PreDestroy;
    @Component
    public class BeanLifeComponent implements BeanNameAware {
        @PostConstruct
        public void postConstruct() {
            System.out.println("执⾏ PostConstruct()");
        }
        public void init() {
            System.out.println("执⾏ BeanLifeComponent init-method");
        }
        @PreDestroy
        public void preDestroy() {
            System.out.println("执⾏:preDestroy()");
        }
        public void setBeanName(String s) {
            System.out.println("执⾏了 setBeanName ⽅法:" + s);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    实例化和初始化的区别:
    实例化和属性设置是 Java 级别的系统“事件”,其操作过程不可⼈工预和修改;而初始化是给开发者提供的,可以在实例化之后,类加载完成之前进行自定义“事件”处理

    八,哪些是重要的bean生命周期方法? 你能重载它们吗

    • 有两个重要的bean 生命周期方法,第一个是setup , 它是在容器加载bean的时候被调用。第二个方法是 teardown 它是在容器卸载类的时候被调用。
    • bean 标签有两个重要的属性(init-method和destroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct和@PreDestroy)
      扩展【什么是Spring的内部bean?什么是Spring inner beans】
      在Spring框架中,当一个bean仅被用作另一个bean的属性时,它能被声明为一个内部bean。内部bean可以用setter注入“属性”和构造方法注入“构造参数”的方式来实现,内部bean通常是匿名的,它们的Scope一般是prototype

    九,在 Spring中如何注入一个java集合

    Spring提供以下几种集合的配置元素:

    • 类型用于注入一列值,允许有相同的值。

    • 类型用于注入一组值,不允许有相同的值。

    • 类型用于注入一组键值对,键和值都可以为任意类型。

    • 类型用于注入一组键值对,键和值都只能为String类型。

    十,使用@Autowired注解自动装配的过程是怎样的

    使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,
    在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:

    • 如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
    • 如果查询的结果不止一个,那么@Autowired会根据名称来查找;
    • 如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

    扩展 【自动装配有哪些局限性】
    重写:你仍需用 和 配置来定义依赖,意味着总要重写自动装配。
    基本数据类型:你不能自动装配简单的属性,如基本数据类型,String字符串,和类。
    模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。

  • 相关阅读:
    Docker数据卷篇
    【LeetCode刷题-滑动窗口】-- 795.区间子数组个数
    C++编译期循环获取变量类型
    HttpClient使用不当,服务挂了,是时候系统学习一下了
    Java+JSP+MySQL基于SSM的会议交接平台的设计与实现-计算机毕业设计
    java语言程序设计教程pdf,java面试简历
    Mysql触发器
    【Head First 设计模式】-- 策略模式
    基于nodejs+vue酒店综合服务[程序+论文+开题]-计算机毕业设计
    asp.net core之日志
  • 原文地址:https://blog.csdn.net/qq_55660421/article/details/126315502