Spring IoC
Spring IoC容器 作用
控制反转即IoC(Inversion of Control),将传统上由程序代码直接控制的对象的调度权交给容器,通过容器来实现对象组件的装配和管理。所谓的”控制反转”概念就是对组件对象控制权的转移,从本身程序代码转到容器,Spring IOC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期)。对于l0C来说,最重要的就是容器。容器管理着Bean的生命周期,控制着Bean的依赖注入。
控制反转(oC)有什么作用
- 管理对象的创建和依赖关系的维护:对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的
- 解耦:由容器去维护具体的对象
- 托管了类的产生过程:比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的
Spring loc的实现机制
Spring中的loC的实现原理就是工厂模式加反射机制。
1 | public interface Fruit { |
Spring的依赖注入(DI) IOC和DI的区别
很多人把1OC和DI说成一个东西,笼统来说的话是没有问题的,但是本质上还是有所区别的,希望大家能够严谨一点, IOC和DI是从不同的角度描述的同一件事, I0C是从容器的角度描述,而DI是从应用程序的角度来描述,也可以这样说, IOC是依赖倒置原则的设计思想,而DI是具体的实现方式。
在面向对象设计的软件系统中,底层的实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。
有一个对象出了问题,就可能会影响到整个流程的正常运转。现在,伴随着工业级应用的规模越来越庞大,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系,因此,架构师和设计师对于系统的分析和设计,将面临更大的挑战。对象之间耦合度过高的系统,必然会出现牵一发而动全身的情形。
![image-20211216224636741](/Users/leslie/Library/Application Support/typora-user-images/image-20211216224636741.png)
大家看到了吧,由于引进了中间位置的”第三方” ,也就是IOC容器,对象和对象之间没有了耦合关系,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个”粘合剂” ,对象与对象之间会彼此失去联系,这就是有人把IoC容器比喻成”粘合剂”的由来。
BeanFactory的作用
- BeanFactory是Spring中非常核心的一个顶层接口;
- 它是Bean的“工厂”、它的主要职责就是生产Bean;
- 它实现了简单工厂的设计模式,通过调用getBean传入标识生产一个Bean;
- 它有非常多的实现类、每个工厂都有不同的职责(单一职责)功能,最强大的工厂是:DefaultListableBeanFactory
- Spring底层就是使用的该实现工厂进行生产Bean的
- BeanFactory它也是容器 Spring容器(管理着Bean的生命周期)
BeanDefinition的作用
主要负责存储Bean的定义信息:决定Bean的生产方式
后续BeanFactory会根据这些信息就行生产Bean:比如实例化,可以通过class进行反射进而得到实例对象
BeanFactory 和 ApplicationContext区别
BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。
依赖关系
BeanFactory:是Spring里面最顶层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。BeanFactory 简单粗暴,可以理解为就是个 HashMap,Key 是BeanName,Value 是 Bean 实例。通常只提供注册(put),获取(get)这两个功能。我们可以称之为 “低级容器”。
ApplicationContext 可以称之为 “高级容器”。因为他比 BeanFactory 多了更多的功能。他继承了多个接口。因此具备了更多的功能。例如资源的获取,支持多种消息(例如 JSP tag 的支持),对 BeanFactory 多了工具级别的支持等待。所以你看他的名字,已经不是 BeanFactory 之类的工厂了,而是 “应用上下文”, 代表着整个大容器的所有功能。
IOC容器的加载过程
从概念态—>定义态的过程
- 实例化一个ApplicationContext的对象;
- 调用bean工厂后置处理器完成扫描;
- 循环解析扫描出来的类信息;
- 实例化一个BeanDefinition对象来存储解析出来的信息;
- 把实例化好的beanDefinition对象put到beanDefinitionMap当中缓存起来,
以便后面实例化bean;再次调用其他bean工厂后置处理器;
从定义态到纯净态
- 当然spring还会干很多事情,比如国际化,比如注册BeanPostProcessor等
等,如果我们只关心如何实例化一个bean的话那么这一步就是spring调用
finishBeanFactoryInitialization方法来实例化单例的bean,实例化之前spring要做验证,需要遍历所有扫描出来的类,依次判断这个bean是否Lazy,是否prototype,是否abstract等等; - 如果验证完成spring在实例化一个bean之前需要推断构造方法,因为spring实例化对象是通过构造方法反射,故而需要知道用哪个构造方法;
- 推断完构造方法之后spring调用构造方法反射实例化一个对象;注意我这里说的是对象;这个时候对象已经实例化出来了,但是并不是一个完整的bean,最简单的体现是这个时候实例化出来的对象属性是没有注入,所以不是一个完整的bean;
从纯净态到成熟态
- spring处理合并后的beanDefinition
- 判断是否需要完成属性注入
- 如果需要完成属性注入,则开始注入属性
初始化
- 判断bean的类型回调Aware接口
- 调用生命周期回调方法
- 如果需要代理则完成代理
创建完成
- put到单例池——bean完成——存在spring容器当中
配置Bean方式
- xml :
- 注解:@Component(@Controller、@Service、@Repostory) 前提:需要配置扫描包
反射调用构造方法 - javaConfig: @Bean 可以自己控制实例化过程
- @import 3中方式
Bean 的完整生命周期
Bean 的生命周期
如上图所示,Bean 的生命周期还是比较复杂的,下面来对上图每一个步骤做文字描述:
- Spring启动,查找并加载需要被Spring管理的bean,进行Bean的实例化
- Bean实例化后对将Bean的引入和值注入到Bean的属性中
- 如果Bean实现了BeanNameAware接口的话,Spring将Bean的Id传递给setBeanName()方法
- 如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
- 如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来。
- 如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法。
- 如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用
- 如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。
- 此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
- 如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用了destory-method 声明销毁方法,该方法也会被调用。
Spring 的Bean的作用域
Spring框架支持以下五种bean的作用域:
- singleton : bean在每个Spring ioc 容器中只有一个实例。
- prototype:一个bean的定义可以有多个实例。
- request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
- session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
- application:全局 Web 应用程序范围的范围标识符。
注意: 缺省的Spring bean 的作用域是Singleton。使用 prototype 作用域需要慎重的思考,因为频繁创建和销毁 bean会带来很大的性能开销
Spring实例化bean方式的几种方式
- 构造器方式(反射);
- 静态工厂方式; factory-method
- 实例工厂方式(@Bean); factory-bean+factory-method
- FactoryBean方式
Spring框架中的单例bean是线程安全的吗
不是,Spring框架中的单例bean不是线程安全的。
spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。
实际上大部分时候 spring bean 无状态的(比如 dao 类),所以某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,
把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。
- 有状态就是有数据存储功能(比如成员变量读写)。
- 无状态就是不会保存数据。