Spring Ioc容器初始化过程

  • 流程简介
  • 启动过程
  • refresh() //初始化入口
  • 1. prepareRefresh() //准备工作
  • 2. BeanFactory beanFactory = obtainFreshBeanFactory()
  • 3.prepareBeanFactory()
  • 4.postProcessBeanFactory(beanFactory)
  • 5.invokeBeanFactoryPostProcessors(beanFactory)
  • 6.registerBeanPostProcessors(beanFactory);
  • 7.initMessageSource();
  • 8. initApplicationEventMulticaster();
  • 9.onRefresh();
  • 10.registerListeners();
  • 11.finishBeanFactoryInitialization(beanFactory);
  • 12.finishRefresh();
  • 常见面试题
  • 1.ApplicationContext,BeanFactory区别
  • 2.BeanFactory&FactoryBean区别
  • 3.Bean生命周期
  • 4.什么是循环依赖
  • 一级缓存能否解决循环依赖?
  • 二级缓存能否解决?
  • BeanFactory 和 FactoryBean的区别?
  • Spring AOP


流程简介

1.创建工厂
2.加载配置文件
3.执行beanFactoryPostProcessor前置化操作
4.利用反射实例化bean
5.初始化bean

  • 填充自定义属性 populateBean -->循环依赖
  • 执行aware接口需要实现的方法
  • BeanPostProcessor:postProcessBeforeInitialization
  • Init-method方法
  • BeanPostProcessor:postProcessAfterInitialization

6.使用对象

7.销毁对象

容器内再次初始化程序使改动 ioc容器初始化的流程_初始化

启动过程

启动类: ClassPathXmlApplicationContext
setConfigLocations(configLoactions)

refresh() //初始化入口

AbstractAppliactionContext.refresh()过程

1. prepareRefresh() //准备工作

  • 设置启动时间
  • 设置容器活跃状态,关闭状态
  • getEnviroment().validateRequiredProperties() //获取环境,加载当前系统的属性到Enviroment对象中
  • 准备监听器和事件的集合对象

2. BeanFactory beanFactory = obtainFreshBeanFactory()

  • 创建beanFactory: DefaultListableBeanFactory
  • 创建并初始化BeanDefinitionReader
  • 把配置文件解析成一个个Bean
  • loadBeanDefinitions() 将 xml 文件转换为 Document 对象,解析为DOM树
  • processBeanDefinition() 读取bean节点
  • 对基本的bean标签属性及子节点进行解析;
  • 将解析得到的BeanDefinitionHolder对象进行修饰,其主要是根据自定义的属性和子标签来进一步丰富当前BeanDefinition的属性;
  • 将BeanDefinition注册到BeanDefinitionRegistry中registerBeanDefinitions() : 以
    k:beanName-v: BeanDefinition
    形式放在一个map中

DefaultListableBeanFactory中维护的beanDefinitionMap

容器内再次初始化程序使改动 ioc容器初始化的流程_三级缓存_02

Spring Bean注册解析
https://www.jianshu.com/p/7f002423aafa https://www.jianshu.com/p/9275437adc9f

3.prepareBeanFactory()

  1. 设置类加载器;
  2. 设置EL表达式解析器(Bean创建完成填充属性时使用)和属性注册解析器
  3. 利用BeanPostProcessor的特性给各种Aware接口的实现类注入ApplicationContext中对应的属性
  4. 设置各种Aware接口的实现类为忽略自动装配
  5. 设置自动装配的类(BeanFactory,ResourceLoader,ApplicationEventPublisher,ApplicationContext)
  6. 如果BeanFactory中存在loadTimeWeaver的bean,那么需要添加动态织入功能
  7. 注册各种可用组件(environment,systemProperties,systemEnvironment)

4.postProcessBeanFactory(beanFactory)

此方法是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化,具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类,可以管理我们的bean工厂内所有的beandefinition(未实例化)数据,可以随心所欲的修改属性

容器内再次初始化程序使改动 ioc容器初始化的流程_三级缓存_03


容器内再次初始化程序使改动 ioc容器初始化的流程_实例化_04

5.invokeBeanFactoryPostProcessors(beanFactory)

这个方法是调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法;

6.registerBeanPostProcessors(beanFactory);

这个方法将 BeanPostProcessor 接口的实现类到注册Ioc容器中

ps: 只是实例化实现BeanPostProcessor 的类实例并且注册到 Ioc 中,即在DefaultListableBeanFactory中beanPostProcessors的List中增加,具体调用BeanPostProcessor的两个放方法是在 bean 初始化的前后

容器内再次初始化程序使改动 ioc容器初始化的流程_初始化_05

容器内再次初始化程序使改动 ioc容器初始化的流程_实例化_06

7.initMessageSource();

方法是初始化当前 ApplicationContext 的 MessageSource,国际化处理

8. initApplicationEventMulticaster();

初始化当前 ApplicationContext 的事件广播器, 默认是SimpleApplicationEventMutilcaster类型

9.onRefresh();

留待扩展

10.registerListeners();

注册所有静态的ApplicationListener类型和实现了ApplicationListener接口的Listener监听器, 注册到SimpleApplicationEventMutilcaster 持有的Set中

容器内再次初始化程序使改动 ioc容器初始化的流程_容器内再次初始化程序使改动_07

11.finishBeanFactoryInitialization(beanFactory);


容器内再次初始化程序使改动 ioc容器初始化的流程_初始化_08

  • 实例化前的准备:注册默认的值解析器;初始化LoadTimeWeaverAware对象;不允许对注册了的BeanDefinition进行修改
  • 获取beanDefinition的副本(允许init方法新的bean定义);区分FactoryBean()和普通Bean的实例化
  • doGetBean方法
  • getSingleton 判断是否已经实例化过了,这里用到了三级缓存
  • 先判断一级缓存有没有,没有返回创建AbstractAutowireCapableBeanFactory.createBean
    AbstractAutowireCapableBeanFactory.createBean
  1. 在实例化Bean前,第一次调用后置处理器postProcessBeforeInstantiation(beanClass, beanName); 见上面👆第6步
    调用了实现InstantiationAwareBeanPostProcessor接口的后置处理器

Ps: @EnableAspectJAutoProxy注解的实现链接

容器内再次初始化程序使改动 ioc容器初始化的流程_三级缓存_09

  1. AbstarctAutowireCapableBeanFactory.doCreateBean创建Bean
  • 创建一个 BeanWrapper,用来存放bean+其他属性 instanceWrapper = createBeanInstance(beanName, mbd, args);
  • 检测一个类的访问权限, Spring默认是 允许访问非public类型的方法
  • 如果是需要自动注入的,就使用构造方法自动注入
  • 否则通过默认的无参构造方法进行
  • 创建bean的实例,封装进上面的BeanWrapper中
  • 分两次调用处理器
  • 设置属性,填充属性 populateBean填充属性,如果存在依赖的bean没有初始化则递归的初始化
  • 经过AOP处理,将原生对象转换成Proxy
  • 返回BeanWrapper
  • InitializeBean执行后置处理器,aop就是在这里完成的处理
  • invokeAwareMethods(beanName,bean) 处理aware接口
  • applyBeanPostProcessorsBeforeInitialization 在实例化Bean前,第一次调用后置处理器
  • invokeInitMethods
  • applyBeanPostProcessorsAfterInitialization

12.finishRefresh();

广播事件,ApplicationContext 初始化完成

常见面试题

1.ApplicationContext,BeanFactory区别

①ApplicationContext在注册Bean之后还会立即初始化各个Bean的实例,BeanFactory只有在调用getBean()方法时才会开始实例化各个Bean;

②ApplicationContext会自动检测配置文件中声明的BeanFactoryPostProcessor和BeanPostProcessor等实例,并且在实例化各个Bean的时候会自动调用这些配置文件中声明的辅助bean实例,而BeanFactory必须手动调用其相应的方法才能将声明的辅助Bean添加到IoC容器中。

容器内再次初始化程序使改动 ioc容器初始化的流程_初始化_10

2.BeanFactory&FactoryBean区别

BeanFactory是个Factory,也就是IOC容器或对象工厂,在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的,此BeanFactory是个接口,spring提供了很多实现,如ApplicationContext, 具有实例化、定位、配置应用程序中的对象及建立这些对象间的依赖的功能
基本方法:

  • boolean containsBean(String beanName) 判断工厂中是否包含给定名称的bean定义,若有则返回true
  • Object getBean(String) 返回给定名称注册的bean实例。根据bean的配置情况,如果是singleton模式将返回一个共享实例,否则将返回一个新建的实例,如果没有找到指定bean,该方法可能会抛出异常
  • Object getBean(String, Class) 返回以给定名称注册的bean实例,并转换为给定class类型
  • Class getType(String name) 返回给定名称的bean的Class,如果没有找到指定的bean实例,则排除NoSuchBeanDefinitionException异常
  • boolean isSingleton(String) 判断给定名称的bean定义是否为单例模式
  • String[] getAliases(String name) 返回给定bean名称的所有别名

FactoryBean是个Bean,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,如果一个类实现了 FactoryBean 接口,则该类可以自己定义创建实例对象的方法,只需要实现它的 getObject() 方法

一般情况下,Spring通过反射机制利用bean的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在bean中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean<T>接口的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在id前面加一个 & 符号来获取。

容器内再次初始化程序使改动 ioc容器初始化的流程_三级缓存_11


BeanFactory 简介以及它 和FactoryBean的区别(阿里面试)

3.Bean生命周期

(1)实例化Bean:
对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。

(2)设置对象属性(依赖注入):
实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。

(3)处理Aware接口:
接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:

  • 如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值;
  • 如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
  • 如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;

(4)postProcessBeforeInitialization:
如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。

(5)InitializingBean 与 init-method:
如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。

(6)postProcessAfterInitialization
如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;
以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。

(7)DisposableBean:
当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;

(8)destroy-method:
最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法

4.什么是循环依赖

场景:

无法解决:

  • 构造器的循环依赖: A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象(此方式实例化时候调用构造方法会陷入循环)
    可以解决:(实例化和初始化分开,B作为A的一个属性,在A实例化之后才开始赋值)
  • A的构造方法中依赖了B的实例对象,同时B的某个field或者setter需要A的实例对象,以及反之
  • A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象,以及反之

怎么检测:Bean在创建的时候可以给该Bean打标,如果递归调用回来发现正在创建中的话,即说明了循环依赖了

怎么解决循环依赖:三级缓存
Spring的单例对象的初始化主要分为三步:会发生循环依赖的在一二两步
(1)实例化,其实也就是调用对象的构造方法实例化对象
(2)populateBean:填充属性,这一步主要是多bean的依赖属性进行填充
(3)初始化

/** Cache of singleton objects 单例对象的cache,一级缓存,存放成品对象 : bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

/** Cache of early singleton objects 提前暴光的单例对象的Cache,二级缓存存放getEarlyBeanReference形成的半成品对象(原生或代理类对象): bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

/** Cache of singleton factories 单例对象工厂的cache 三级缓存完成代理对象的覆盖过程: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

解决循环依赖的关键:初始化和实例化分开操作,在为对象属性赋值时可以使用半成品对象(实例化却没有完成初始化的对象)

A<—>B, A依赖B, B依赖A

  1. 创建A
    doGetBean ->
    getSingleton(beanName) 从一二三级缓存查询,第一次创建A,查询不到 ->
    getSingleton(beanName, () ->
    {return createBean(beanName, mbd, args);}) ->
    doCreateBean->createBeanInstance->ctor.newInstance(args)
  2. 容器内再次初始化程序使改动 ioc容器初始化的流程_初始化_12

  3. 将A放在三级缓存:目的-将实例化完成但未填充属性(未初始化的)A提前暴露出来
    Map<String, ObjectFactory<?>> singletonFactories : beanName(a) -> ObjectFactory::getEarlyBeanReference(此时还没有调用这个方法)
  4. 容器内再次初始化程序使改动 ioc容器初始化的流程_实例化_13

  5. 填充A的属性,开始创建A依赖的属性B
    populateBean->applyPropertyValues->doGetBean同上createBeanInstance->ctor.newInstance(args)->将B放在三级缓存
  6. 填充B的属性A
    populateBean->applyPropertyValues->doGetBean获取A
    这里doGetBean再次调用getSingleton(beanName) 从一二三级缓存查询A,此时能从三级缓存查到半成品A的beanName和对应的ObjectFactoty, 调用getObject此时第一次调用三级缓存放进的getEarlyBeanReference:直接返回半成品A/返回代理对象A->将A从三级缓存移动到二级缓存->为B附属性值A
  7. 容器内再次初始化程序使改动 ioc容器初始化的流程_容器内再次初始化程序使改动_14

  8. B实例化完成,执行初始化逻辑 initializeBean
  • invokeAwareMethods(beanName, bean);处理aware接口
  • applyBeanPostProcessorsBeforeInitialization
  • invokeInitMethods
  • applyBeanPostProcessorsAfterInitialization
  1. addSingleton 把实例化且初始化完成的B从三级缓存移动到一级缓存
    []doGetBean方法:
  2. 容器内再次初始化程序使改动 ioc容器初始化的流程_三级缓存_15

    容器内再次初始化程序使改动 ioc容器初始化的流程_三级缓存_16


  3. 容器内再次初始化程序使改动 ioc容器初始化的流程_初始化_17

  4. B成品实例化完成返回,A赋值属性,A实例化完成,执行初始化逻辑同上
  5. addSingleton 把实例化且初始化完成的A从二级缓存移动到一级缓存
一级缓存能否解决循环依赖?

只有一级缓存,在并发场景下可能获取到半成品对象,无法直接使用

二级缓存能否解决?

如果不调用getEarlyBeanReference创建代理对象,而是全都使用构造器实例化的对象,二级缓存可以,否则原生的bean和代理bean可能同时出现,从二级缓存中可能前面获取的原生bean,过一会获取的代理bean

BeanFactory 和 FactoryBean的区别?
  • BeanFactory是个Factory,也就是IOC容器或对象工厂,在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的,提供了实例化对象和拿对象的功能。
  • FactoryBean是个Bean,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。
Spring AOP

简述:
AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
OOP是关注将需求功能划分为不同的并且相对独立,封装良好的类,并让它们有着属于自己的行为,依靠继承和多态等来定义彼此的关系;AOP是希望能够将通用需求功能从不相关的类当中分离出来,能够使得很多类共享一个行为,一旦发生变化,不必修改很多类,而只需要修改这个行为即可。
AOP是使用切面(aspect)将横切关注点模块化,OOP是将状态和行为模块化。

代理:指为一个目标对象提供一个代理对象, 并由代理对象控制对目标对象的引用. 使用代理对象, 是为了在不修改目标对象的基础上, 增强目标对象的业务逻辑

静态代理:目标类与代理类实现同一个接口,让代理类持有真实类对象,在代理类方法中调用真实类方法,在调用真实类方法前后添加我们所需要的功能扩展代码
扩展性弱,不利于维护
动态代理:

代理对象生成:

spring提供两种JDK动态代理和CGLib动态代理,默认如果目标类是接口使用JDK,否则使用CGLib代理

getEarlyBeanReference->createProxy->getProxy->proxy.newProxyInstance

容器内再次初始化程序使改动 ioc容器初始化的流程_初始化_18

切面织入:
InvocationHandler是JDK动态代理的核心,生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法。而通过JdkDynamicAopProxy的签名我们可以看到这个类其实也实现了InvocationHandler,下面我们就通过分析这个类中实现的invoke()方法来具体看下Spring AOP是如何织入切面的。