Spring IoC

Spring IoC容器 作用

控制反转即IoC(Inversion of Control),将传统上由程序代码直接控制的对象的调度权交给容器,通过容器来实现对象组件的装配和管理。所谓的”控制反转”概念就是对组件对象控制权的转移,从本身程序代码转到容器,Spring IOC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期)。对于l0C来说,最重要的就是容器。容器管理着Bean的生命周期,控制着Bean的依赖注入。

控制反转(oC)有什么作用

  • 管理对象的创建和依赖关系的维护:对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的
  • 解耦:由容器去维护具体的对象
  • 托管了类的产生过程:比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的

Spring loc的实现机制

Spring中的loC的实现原理就是工厂模式加反射机制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public interface Fruit {
public abstract void eat();
}

public class Apple implements Fruit{
@Override
public void eat() {
System.out.println("Apple");
}
}

public class Orange implements Fruit{
@Override
public void eat(){
System.out.println("Orange");
}
}

public class Factory {
public static Fruit getInstance(String ClassName){
Fruit fruit = null;
try {
fruit = (Fruit) Class.forName(ClassName).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return fruit;
}
}

public class Hello {
public static void main(String[] args) {
Fruit fruit = Factory.getInstance("leslie.IoC.Apple");
if (fruit != null){
fruit.eat();
}
}
}

Spring的依赖注入(DI) IOC和DI的区别

很多人把1OC和DI说成一个东西,笼统来说的话是没有问题的,但是本质上还是有所区别的,希望大家能够严谨一点, IOC和DI是从不同的角度描述的同一件事, I0C是从容器的角度描述,而DI是从应用程序的角度来描述,也可以这样说, IOC是依赖倒置原则的设计思想,而DI是具体的实现方式。

在面向对象设计的软件系统中,底层的实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。

image-20211216224514639

有一个对象出了问题,就可能会影响到整个流程的正常运转。现在,伴随着工业级应用的规模越来越庞大,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系,因此,架构师和设计师对于系统的分析和设计,将面临更大的挑战。对象之间耦合度过高的系统,必然会出现牵一发而动全身的情形。

![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 之类的工厂了,而是 “应用上下文”, 代表着整个大容器的所有功能。image-20211217224330496

IOC容器的加载过程

从概念态—>定义态的过程

  1. 实例化一个ApplicationContext的对象;
  2. 调用bean工厂后置处理器完成扫描;
  3. 循环解析扫描出来的类信息;
  4. 实例化一个BeanDefinition对象来存储解析出来的信息;
  5. 把实例化好的beanDefinition对象put到beanDefinitionMap当中缓存起来,
    以便后面实例化bean;再次调用其他bean工厂后置处理器;

从定义态到纯净态

  1. 当然spring还会干很多事情,比如国际化,比如注册BeanPostProcessor等
    等,如果我们只关心如何实例化一个bean的话那么这一步就是spring调用
    finishBeanFactoryInitialization方法来实例化单例的bean,实例化之前spring要做验证,需要遍历所有扫描出来的类,依次判断这个bean是否Lazy,是否prototype,是否abstract等等;
  2. 如果验证完成spring在实例化一个bean之前需要推断构造方法,因为spring实例化对象是通过构造方法反射,故而需要知道用哪个构造方法;
  3. 推断完构造方法之后spring调用构造方法反射实例化一个对象;注意我这里说的是对象;这个时候对象已经实例化出来了,但是并不是一个完整的bean,最简单的体现是这个时候实例化出来的对象属性是没有注入,所以不是一个完整的bean;

从纯净态到成熟态

  1. spring处理合并后的beanDefinition
  2. 判断是否需要完成属性注入
  3. 如果需要完成属性注入,则开始注入属性

初始化

  1. 判断bean的类型回调Aware接口
  2. 调用生命周期回调方法
  3. 如果需要代理则完成代理

创建完成

  1. put到单例池——bean完成——存在spring容器当中

配置Bean方式

  • xml :
  • 注解:@Component(@Controller、@Service、@Repostory) 前提:需要配置扫描包 反射调用构造方法
  • javaConfig: @Bean 可以自己控制实例化过程
  • @import 3中方式

Bean 的完整生命周期

深究Spring中Bean的生命周期

Bean 的生命周期

如上图所示,Bean 的生命周期还是比较复杂的,下面来对上图每一个步骤做文字描述:

  1. Spring启动,查找并加载需要被Spring管理的bean,进行Bean的实例化
  2. Bean实例化后对将Bean的引入和值注入到Bean的属性中
  3. 如果Bean实现了BeanNameAware接口的话,Spring将Bean的Id传递给setBeanName()方法
  4. 如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
  5. 如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来。
  6. 如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法。
  7. 如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用
  8. 如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。
  9. 此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
  10. 如果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方式的几种方式

  1. 构造器方式(反射);
  2. 静态工厂方式; factory-method
  3. 实例工厂方式(@Bean); factory-bean+factory-method
  4. FactoryBean方式

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

不是,Spring框架中的单例bean不是线程安全的。
spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。
实际上大部分时候 spring bean 无状态的(比如 dao 类),所以某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,

把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。

  • 有状态就是有数据存储功能(比如成员变量读写)。
  • 无状态就是不会保存数据。