Bean的生命周期、作用域

一、Bean的生命周期:

Spring中的一个Bean生命周期总体分为以下几个阶段:

在这里插入图片描述

二、细化Bean的生命周期:

在这里插入图片描述
从上面可以看出,可初步分为四个阶段:

  1. Bean的实例化阶段(通过反射创建对象)
  2. Bean的设置属性阶段(属性值非自动装配)
  3. Bean的初始化阶段(如数据源赋值、校验属性)
  4. Bean的销毁阶段(ioc容器销毁关闭,关闭数据源)
  • 在设置属性阶段后,postProcessBeforeInitialization方法执行前,会执行很多Aware类型的接口,这种类型接口作用是加载资源到Spring容器中,让bean获取Spring容器中的服务。

  • 在初始化阶段,有个特别重要的接口BeanPostProcessor(Bean处理器),在初始化前、后调用:
    在这里插入图片描述

  • 初始化方式有三个,分别是:
    1.InitializingBean的afterPropertiesSet方法
    2.@PostConstruct注解标注的方法
    3.配置的init-method

  • 容器销毁的方式有三个,分别是:
    1.@PreDestroy注解标注的方法
    2.DisposableBean接口的destroy()方法
    3.配置的destroy-method

三、总结Bean生命周期:

  1. Bean容器找到Spring配置文件中Bean的定义
  2. Bean容器利用java 反射机制实例化Bean
  3. Bean容器为实例化的Bean 设置属性值
  4. 如果Bean实现了BeanNameAware接口,则执行setBeanName方法;
  5. 如果Bean实现了BeanClassLoaderAware接口,则执行setBeanClassLoader方法;
  6. 如果Bean实现了BeanFactoryAware接口,则执行setBeanFactory方法;
  7. 如果Bean实现了xxxAware接口…;
  8. 如果Bean实现了ApplicationContextAware接口,则执行setApplicationContext方法;
  9. 如果加载了BeanPostProcessor相关实现类,则执行postProcessBeforeInitialization方法(前置处理器)
  10. 如果Bean定义初始化方法(PostConstruct注解、配置init-method、实现了InitializingBean接口),则执行定义的初始化方法
  11. 如果加载了BeanPostProcessor相关实现类,则执行postProcessAfterInitialization方法(后置处理器)
  12. 当要销毁这个Bean时,如果自定义了销毁方法(PreDestroy注解、配置destroy-method、实现了DisposableBean接口),则执行定义的销毁方法

四、Bean的作用域:

spring 支持 5 种作用域,如下:

  • singleton:唯一bean实例,Spring中的bean默认都是单例的。

  • prototype:每次请求都会创建一个新的bean实例。

  • request:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。

  • session:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。

  • global-session:全局session作用域,仅仅在基于Portlet的Web应用中才有意义,Spring5中已经没有了。Portlet是能够生成语义代码(例如HTML)片段的小型Java Web插件。它们基于Portlet容器,可以像Servlet一样处理HTTP请求。但是与Servlet不同,每个Portlet都有不同的会话。

五、Bean是线程安全的吗?

Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究

结合上面提到的Spring bean 的作用域(scope)

  • 对于prototype作用域的Bean,每次都创建一个新对象,也就是线程之间不存在Bean共享,因此不会有线程安全问题。

  • 对于singleton作用域的Bean,所有的线程都共享一个单例实例的Bean,因此是存在线程安全问题的。但是如果单例Bean是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Controller类、Service类和Dao等,这些Bean大多是无状态的,只关注于方法本身。

    • 有状态Bean(Stateful Bean) :就是有实例变量的对象,可以保存数据,是非线程安全的。
    • 无状态Bean(Stateless Bean):就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的。
  • 对于singleton作用域的有状态Bean(比如ModelAndView),就需要自行保证线程安全,最浅显的解决办法就是将有状态的bean的作用域由“singleton”改为“prototype”。也可以采用ThreadLocal解决线程安全问题,为每个线程提供一个独立的变量副本,不同线程只操作自己线程的副本变量。

ThreadLocal线程同步机制都是为了解决多线程中相同变量的访问冲突问题。

  • 同步机制:采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。

  • ThreadLocal:采用了“空间换时间”的方式。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。

六、Spring bean为什么默认是单例

(1)单例bean与原型bean的区别

  • 如果一个bean被声明为单例的时候,在处理多次请求的时候在Spring容器里只实例化出一个bean,后续的请求都公用这个对象,这个对象会保存在一个map里面。当有请求来的时候会先从缓存(map)里查看有没有,有的话直接使用这个对象,没有的话才实例化一个新的对象,所以这是个单例的。

  • 但是对于原型(prototype)bean来说当每次请求来的时候直接实例化新的bean,没有缓存以及从缓存查的过程。

1. 画图分析在这里插入图片描述
2. 源码分析

生成bean时先判断单例的还是原型的

在这里插入图片描述

如果是单例的则先尝试从缓存里获取,没有在新创建

在这里插入图片描述

3. 结论:
&esmp;&esmp;单例的bean只有第一次创建新的bean 后面都会复用该bean,所以不会频繁创建对象原型的bean每次都会新创建。

(2)单例bean的优势

由于不会每次都新创建新对象所以有一下几个性能上的优势。

  1. 减少了新生成实例的消耗

新生成实例消耗包括两方面,第一,spring会通过反射或者cglib来生成bean实例这都是耗性能的操作,其次给对象分配内存也会涉及复杂算法。

  1. 减少jvm垃圾回收

由于不会给每个请求都新生成bean实例,所以自然回收的对象少了。

  1. 可以快速获取到bean

因为单例的获取bean操作除了第一次生成之外其余的都是从缓存里获取的所以很快。

(3)单例bean的劣势

单例的bean一个很大的劣势就是他不能做到线程安全!由于所有请求都共享一个bean实例,所以这个bean要是有状态的一个bean的话可能在并发场景下出现问题,而原型的bean则不会有这样问题(但也有例外,比如他被单例bean依赖),因为给每个请求都新创建实例。

(4)总结

  1. Spring 为啥把bean默认设计成单例?为了提高性能少创建实例垃圾回收缓存快速获取

  2. 单例有啥劣势?如果是有状态的话在并发环境下线程不安全

  3. 什么是有状态对象?什么是无状态对象?

    • 有状态对象:有实例变量可以标志其对象所处的状态。(有实例变量的对象,有存储数据能力)- 白话:有属性的对象
    • 无状态对象:无实例变量可以标志其对象所处的状态。(无实例变量的对象,无存储数据能力)- 白话:无属性的对象
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值