IOC

IOC原理

IOC(Inversion of Control):控制反转

  • Spring Core最核心的部分
  • 需要先了解依赖注入(Dependency Inversion)

依赖注入含义

把底层类作为参数传递给上层类,实现上层对下层的“控制”

使用依赖注入的代码实例

截屏2021-01-31 下午9.17.08

需要修改轮胎时只用改一下轮胎就行了,不再需要大动干戈

截屏2021-01-31 下午9.17.53

IOC、DI、DL的关系

DL(Dependency Lookup):依赖查找(已经被抛弃,需要用户自己使用API查找对象)

截屏2021-01-31 下午9.45.28

依赖注入的方式

  • Setter
    • 实现特定属性的pulic setter来让IOC容器调用注入所依赖类型的对象
  • Interface
    • 实现特定的接口以供IOC容器注入所依赖类型的对象
  • Constructor
    • 基于构造函数,实现特定参数的构造函数,在创建对象时让IOC容器注入所依赖类型的对象
  • Annotation
    • 基于注解,通过Java的注解机制来让IOC容器注入所依赖类型的对象
    • Autowired

依赖倒置原则、IOC、DI、IOC容器的关系

截屏2021-02-01 下午6.59.27
  • 依赖倒置原则是一种思想:高层模块不应该依赖低层模块,两者都应该依赖其抽象
  • 前图那一大堆new就是IOC容器可以帮我们做的事

IOC容器的优势

  • 避免在各处使用new来创建类,并且可以做到维护统一
  • 创建示例时不需要了解其中的细节
    • 没有使用IOC容器时候需要自己一个一个new,需要理清整个流程截屏2021-02-01 下午7.04.14
    • 使用IOC容器后在进行这个工作是反过来的,从最上层往下查找依赖关系,再从最下层往上一步一步去new,IOC容器可以隐藏具体创建实例的细节,蓝色部分即为被隐藏部分。截屏2021-02-01 下午7.06.38

Spring的IOC容器

截屏2021-02-01 下午7.10.10
  1. Spring启动时读取应用程序的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表
  2. 根据这张注册表去实例化Bean
  3. 装配好Bean之间的依赖关系,为上层提供准备就绪的运行环境
    1. Spring提供一个配置文件描述Bean,Bean之间的依赖关系
    2. 利用Java语言的反射功能实例化Bean,并建立Bean之间的依赖关系。

Spring IOC支持的功能

  • 依赖注入
  • 依赖检查
  • 自动装配
  • 支持集合
  • 指定初始化方法和销毁方法
  • 支持回调方法
    • 需要实现Spring接口
    • 应谨慎使用

Spring IOC容器的核心接口

  • BeanFactory
  • ApplicationContext

Spring中的核心接口和类

BeanDefinition
  • 主要用来描述Bean的定义

  • 截屏2021-02-02 上午10.18.24
  • 如上图,Spring启动时会将XML或注解中Bean的定义解析成Spring内部的BeanDefinition

BeanDefinitionRegistry
  • 提供向IOC容器注册BeanDefinition对象的方法
BeanFactory:Spring框架最核心的接口
  • 提供IOC的配置机制

  • 包含Bean的各种定义,便于实例化Bean

  • 在实例化时建立Bean之间的依赖关系,将Bean自身从Bean客户端的配置中解放出来

  • Bean生命周期控制

  • BeanFactory体系结构(了解即可):截屏2021-02-02 上午10.25.34

    • ListableBeanFacotry:定义了访问容器中Bean基本信息的若干方法,如查看Bean的个数,获取某类型Bean的配置名,查看容器中是否包括某一Bean等方法。
    • HierachicalBeanFacorty:父子级联IOC容器的接口,可以通过接口方法访问父容器。通过此接口,Spring可以建立父子层级的容器关联体系,子容器可以访问父容器中的Bean,但是父容器不能访问子容器。Spring使用父子容器实现了很多功能,如在Spring MVC中,展现层的Bean位于一个子容器中,而业务层和持久层的Bean位于父容器中,这样展现层的Bean就可以用业务层和持久层的Bean,而业务层和持久层就看不到展现层的Bean。
    • ConfigurableBeanFactory:增强了IOC容器的可定制性,定义了设置类装载器,属性编辑器以及属性初始化后置处理器等方法
    • AutowireCapableBeanFactory:定义了将容器中的Bean按某种规则进行自动装配(如名字等)
    • SingletonBeanRigistry:允许在运行期间向容器注册Singleton实例Bean的方法
  • BeanFactory源码解析:

    • 各种getBean()方法,以供按照不同规则查找Bean,如名字或类别
    • isSingleton(String var1)方法,判断Bean是否单例存在, 默认情况下Bean都是单例存在

BeanFactory与ApplicationContenxt比较

由于BeanFactory功能还不够强大,Spring在Beanfactory基础上还设计了一个更为高级的接口,即ApplicationContenxt,其是BeanFactory的子接口之一

  • BeanFactory是Spring框架的基础设施,面向Spring
  • ApplicationContenxt面向使用Spring框架的开发者

ApplicationContenxt的功能(继承了多个接口)

  • 继承了BeanFactory接口,能够管理、装配Bean
  • 继承了ResourcePatternResolver,能够加载资源文件
  • 继承了MessageSource,能够实现国际化等功能
  • 继承了ApplicationEventPublisher,能够发布事件、注册监听器,实现监听机制
截屏2021-02-02 上午11.00.17
  • SpringBoot中run()方法一路查看源码,最后会发现创建的就是上图最下面的容器 截屏2021-02-02 上午11.09.04

Spring IOC代码实例

通过ApplicationContext获取Bean

显式装配Bean

首先写一个配置类配置Bean实例规则,给Person的Bean设置名字和Id

截屏2021-02-02 上午11.32.10

run()方法的返回值是ApplicationContext的子类,可以用ApplicationContext来接收并在其中查找Bean。

截屏2021-02-02 上午11.33.10

SpringBoot方式装配Bean

SpringBoot允许扫描装配Bean到IOC容器中,其注解是@Component@ComponentScan

在实体类中使用@Component标明此类要被扫描进入Spring IOC容器中

截屏2021-02-02 上午11.42.44

@ComponentScan用来去扫描这些已经标记好的Bean,@SpringBootApplication已经自带了@ComponentScan所以不用我们再写。

@ComponentScan默认只会扫描当前package和其子package。如果要扫描别的package需要指定一下扫描路径。

如何实现依赖注入

在需要注入的成员变量上标注@Autowired即可

截屏2021-02-02 上午11.49.53

@Autowired会根据属性的类型找到对应的Bean进行注入,其注入的机制默认是根据类型。

如果同时有两个同类型的Bean则会报错,可以用名字注入或者在某个Bean上加@Primary,在多个Bean可选时优先选择它。

Spring IOC refreshContext源码

image-20210203084342540
首先进行prepareRefresh():
image-20210203084441228
  1. 设置容器的启动时间
  2. 撤销关闭状态,开启活跃状态
  3. 验证环境信息中必须存在的属性
obtainFreshBeanFactory()

用来获取BeanFactory实例

prepareBeanFactory(beanFactory)

对BeanFactory进行相关的设置,包括classLoader,表达式解析器等

postProcessBeanFactory(beanFactory)

在BeanFactory设置之后再进行一些后续操作,不同的容器操作不同,故此方法是空的,方便不同的容器去改写此方法

invokeBeanFactoryPostProcessors(beanFactory)

比较重要,逻辑比较复杂,主要是调用工厂后处理器处理各类Bean标签,扫描Bean文件并解析成一个个Bean。

registerBeanPostProcessors(beanFactory)

从Spring容器中找出实现BeanPostProcessor接口的Bean,并设置到BeanFactory的属性中,之后Bean被实例化时会调用BeanPostProcessor即Bean的后置处理器。(和AOP有相关)

initMessageSource()

初始化国际化相关方法

initApplicationEventMulticaster()

初始化事件广播器,用于事件发布

onRefresh()

方法体为空,是一个模版方法,不同的Spring容器会重写此方法做不同的事情,如Web程序的容器会在此创建内置Servlet容器(如tomcat)

registerListeners()

注册事件监听器

finishBeanFactoryInitialization(beanFactory)

实例化BeanFactory中已经被注册但是未被实例化的所有Bean(懒加载除外)

finishRefresh()

最后一个步骤,初始化生命周期处理器等事情。

Refresh方法总结:

  • 为IOC容器以及Bean的生命周期管理提供条件
  • 刷新Spring上下文信息,定义Spring上下文加载流程

Spring IOC getBean()方法

getBean()用来加载Bean,在BeanFactory下有很多getBean()方法,其实现主要在AbstractBeanFactory

首先获取BeanName

根据BeanName调用getSingleton方法获取Bean实例(也可能是工厂实例,获取实例后调用getObjectForBeanInstance方法试着从缓存或实例工厂中获取实例

如果从getSingleton方法中获取不到就尝试从ParentBeanFactory中去获取

。。。

然后会将对类的定义(Defination)如子类父类的定义等进行合并,将父类等属性合并到子类中去

如果存在依赖,递归实例化依赖

判断这个Bean是否是Singleto,如果是就返回单例,否则直接创建

最后做类型检查,创建的Bean是否是需要的Bean,完成后返回。

getBean()方法代码逻辑

  1. 转换BeanName
  2. 从缓存中加载实例
  3. 实例化Bean
  4. 检查ParentBeanFactory
  5. 初始化依赖的Bean
  6. 创建Bean

相关面试题

Spring Bean作用域(scope)

  • singleton:Spring的默认作用域,容器里拥有唯一的Bean实例
  • prototype:针对每个getBean()请求,容器都会创建一个Bean实例
    • 三思,频繁创建和销毁Bean开销较大
如果是web容器,则还支持三种作用域
  • request:会为每个http请求创建一个Bean实例
  • session:会为每个sesison创建一个Bean实例
  • globalSession:会为每个全局http session创建一个Bean实例,该作用域仅对Portlet有效
    • globalSession是Portlet规范提出的概念
    • 该sesison对所有构成某个Portlet web应用的各种不同Portlet所共享。
    • 故仅在使用Portlet时有效

Spring Bean的生命周期

  1. Bean 容器找到配置文件中 Spring Bean 的定义。
  2. Bean 容器利用 Java Reflection API 创建一个Bean的实例
    1. 可能有循环依赖的问题(一级缓存是单例缓存池(singletonObjects),二级缓存是早期对象(earlySingletonObjects),三级缓存是一个包裹对象ObjectFactory(registeredSingletons),通过getObject获取到早期对象。)
    2. 两级可以解决循环依赖,三级为了解决切面的问题
    3. img
    4. 但是IOC无法解决两种循环依赖,一种是非单例对象的,因为非单例对象不会放入缓存的。每次都是需要创建。二是通过构造器注入,也无法解决。从上面的流程可以看出,调用构造器创建实例是在createBeanInstance方法,而解决循环依赖是在populateBean这个方法中,执行顺序也决定了无法解决该种循环依赖
  3. 如果涉及到一些属性值 利用 set()方法设置一些属性值。
  4. 如果发现实现了 *.Aware接口,就调用相应的方法。
    1. 如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入Bean的名字。
    2. 如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。
    3. BeanFactoryAware
  5. 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessBeforeInitialization() 方法
  6. 如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法。
  7. 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
  8. 如果有和加载这个 Bean的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessAfterInitialization() 方法
  9. 当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
  10. 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。

AOP创建Proxy对象的地方有两个:

  • 常规一点的就是在BeanFactory.initializeBean()方法里面调用初始化方法之后,调用applyBeanPostProcessorsAfterInitialization() –> AbstractAutoProxyCreator.wrapIfNecessary()生成proxy对象。
  • 还有就是在存在循环依赖的时候,在doCreateBean() –> addSingletonFactory(beanName, () –> getEarlyBeanReference(beanName, mbd, bean)); –> 然后在getSingleton()的时候从三级缓存中取对象的时候,会调用getEarlyBeanReference() –> AbstractAutoProxyCreator.wrapIfNecessary()生成proxy对象。
Spring Bean 生命周期 preview

循环依赖 + AOP

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
39
40
41
42
43
Copyprotected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
// 1
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
final Object bean = instanceWrapper.getWrappedInstance();

if (earlySingletonExposure) {
// 2
addSingletonFactory(beanName, new ObjectFactory() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}

// 3
Object exposedObject = bean;
// 4
populateBean(beanName, mbd, instanceWrapper);

// 5
if (exposedObject != null) {
exposedObject = initializeBean(beanName, exposedObject, mbd);
}

if (earlySingletonExposure) {
// 6
Object earlySingletonReference = getSingleton(beanName, false);

if (earlySingletonReference != null) {
// 7
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// 8
...
}
}
}

return exposedObject;
}
  • 1处,创建bean对象,此时,属性什么的全是null,可以理解为,只是new了,field还没设置

  • 2处,添加到第三级缓存;加进去的,只是个factory,只有循环依赖的时候,才会发挥作用

  • 3处,把原始bean,存到exposedObject

  • 4处,填充属性;循环依赖情况下,A/B循环依赖。假设当前为A,那么此时填充A的属性的时候,会去:

    new B;

    填充B的field,发现field里有一个是A类型,然后就去getBean(“A”),然后走到第三级缓存,拿到了A的ObjectFactory,然后调用ObjectFactory,然后调用AOP的后置处理器类:getEarlyBeanReference,拿到代理后的bean(假设此处切面满足,要创建代理);

    经过上面的步骤后,B里面,field已经填充ok,其中,且填充的field是代理后的A,这里命名为proxy A。

    B 继续其他的后续处理。

    B处理完成后,被填充到当前的origin A(原始A)的field中

  • 5处,对A进行后置处理,此时调用aop后置处理器的,postProcessAfterInitialization;前面我们说了,此时不会再去调用wrapIfNecessary,所以这里直接返回原始A,即 origin A

  • 6处,去缓存里获取A,拿到的A,是proxy A

  • 7处,我们梳理下:

    exposedObject:origin A

    bean:原始A

    earlySingletonReference: proxy A

    此时,下面这个条件是满足的,所以,exposedObject,最终被替换为proxy A:

    1
    2
    3
    Copyif (exposedObject == bean) {
    exposedObject = earlySingletonReference;
    }