IOC容器流程分析

151 阅读7分钟
一个哥们自己的互联网公司,招技术总监,让我帮他把把关;面试的大佬形象不错,尤其是“发型”完全满足标准;先热热身,经过一番简单介绍以及活跃一下现场气氛后直接切入主题:
.........此处省去一些无关紧要的问题
问:对Spring的了解;
答:AOP、IOC等;
问:ApplicationContext是什么?
答:是一个上下文接口,是Spring的一个类体系结构的体现;通过Spring内置提供的实现类可以启动Spring容器。................此处再次省去一堆概念;
问:请描述一下IOC的启动流程?
答:.....沉默中,没有回答;(PS:还好没有回答写个main方法加个注解就可以了,不然的话肯定一口老血要喷在对方的脸上);
十几秒后果断换一个问题;
问:FactoryBean与BeanFactory的区别
答:没啥区别,都是生产Bean的!(PS:我沉默了);
问:BeanPostProcessor是什么
答:没用过
问:Spring中Bean的生命周期或者加载过程;
答:加入注解后Spring就管理起来了;(PS:又强行忍住了一口老血)
OK!其他的不用问了,寒暄几句,回去等通知吧;

其实这些问题,说难可以非常难,说简单也可以非常简单,简单的来说这些应该是Spring的基础了,之前的文章中说过Spring是一个生态,必定会有很多的拓展点,给予架构人员更多的发挥空间,一问Java的特性,大家都会说:“继承、封装、多态”;作为一个Java多年老嫖客来说,都在考虑自己封装的东西,如何能够委托给Spring进行管理或者说与Spring结合,让以后的开发效率更高;那么了解Spring的原理及流程必定是基础课;


废话不多说,先撸一段代码:

@Configuration
@ComponentScan(basePackages="xinyan.test")
public class TestConfig {

}

public class TestMain {
    public static void main(String[] values){
        final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(TestConfig.class);
    }
}

上面这段代码是不是超简单??????废话,所有语言入门的Hello Word程序都非常简单;先把你骗进来,然后再折磨你,慢慢的让你爱上它;

一、构造方法概述

进入AnnotationConfigApplicationContext的构造方法

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
   /*
    * 调用构造函数,这里是直接调用的无参构造方法,地球人都知道,面向对象编程时调用本地无参构造方法,必定先调用父类的无参构造方法
    * 这里会调用父类(GenericApplicationContext)的无参构造方法
    * 这一个this();就完成了Bean配置读取(BeanDefinitionReader)、Bean扫描(BeanDefinitionSacnner)、Bean注册(BeanDefinitionRegistry)三个核心对象的注册功能
    * 这一步注入了一堆创世纪处理器,这些处理器都是通过这些内置的处理器才能使Spring真正的工作起来
    * */
   this();
   /*
   * 注册我们的配置类
   * 使用第一步初始化完成的AnnotatedBeanDefinitionReader对象进行解析
   * 这一步就是将配置类注册成为Bean定义(BeanDefinition)
   * 注意:这里仅仅是将配置类注册成为Bean定义,解析工作不是这里完成的;
   * 这里注册成为Bing定义那么后面怎么玩都可以了;
   * */
   register(annotatedClasses);
   /*
   * IOC容器刷新接口
   * 这个方法是核心方法;
   * 是整个Spring生态的体现;
   * 体现了IOC的整个生命周期,包括Bean初始化、Bean的加载、使用、销毁等生命周期
   * 此方法中包含13个核心的方法,是Spring这本九阴真经的精髓,了解了这13个方法你才是真正了解了Spring
   * */
   refresh();
}

public AnnotationConfigApplicationContext() {
   /**
    * 初始化注解模式下的bean定义扫描器
    * 调用AnnotatedBeanDefinitionReader构造方法,传入的是this(AnnotationConfigApplicationContext)对象
    */
   this.reader = new AnnotatedBeanDefinitionReader(this);
   /**
    * 初始化我们的classPath类型的bean定义扫描器
    */
   this.scanner = new ClassPathBeanDefinitionScanner(this);
}

GenericApplicationContext类:

public GenericApplicationContext() {
   /**
    * 调用父类的构造函数,简单粗暴的直接为ApplicationContext spring上下文对象初始beanFactory
    * 为什么是DefaultListableBeanFactory?我们去看BeanFactory接口的时候
    * 发DefaultListableBeanFactory是最底层的实现,功能是最全的
    */
   this.beanFactory = new DefaultListableBeanFactory();
}

入下图,其他的源码太深了,已经把我折磨的生不如死,这里也无法详细的展示,想了一下,还是就到这里吧,以后有机会在慢慢来吧;

二、整体流程描述

创世纪处理器是Spring内置的一些核心处理器,用于完成内置资源的解析及处理工作,是整个Spring运作的基石,当然这些核心创世纪处理器在创建Bean的时候是拥有绝对的优先权的;

在筹备阶段,创世纪处理器以及配置类都还仅仅只是一个Bean定义,不能做任何事情,这些只是前戏而已,完成了这一步,代表IOC启动之前的所有工作已经准备完成,可以开始下一步的处理工作了,这里的代码阅读起来头发掉的比较少;后面的核心处理refresh方法才是真正的地狱式的折磨;

这里需要特别注意一下:BeanFactoryPostProcessor这个接口,创世纪处理器ConfigurationClassPostProcessor就是实现了这个接口

ConfigurationClassPostProcessor是专门用来解析我们配置类的,也就是说配置类的解析是通过Bean工厂后置处理器来完成的,这里有一次体现了Spring的生态,Spring之所以强大是因为提供了一个可扩展灵活的生态环境,可以轻松的将各种外部资源进行装载,换而言之:可以说Spring除了IOC以外都是通过这些灵活的扩展点来实现的;

PS:下次再听到哪个小白说Spring就是IOC及AOP的时候,可以装逼的怼一下;

三、Bean的加载及处理

之前的文章中有过描述,BeanFactory是Spring的核心顶级接口之一,在Spring中是通过BeanFactory中的getBean方法来生产Bean的,大致流程如下:

一个比较完整的SpringBean的声明周期如下:

  1. 实例化Bean对象,这个时候仅仅只是一个什么都没有非常低级的Bean而已,没有任何注入属性,基本上无法正常使用;
  2. 填充属性,做完这一步这个Bean基本上算是完整了,因为在这一步会将@Autowired注解的属性已经全部解析并注入完毕,也就是这里完成了依赖注入(DI)的工作;
  3. 如果Bean实现了BeanNameAware接口,调用setBeanName方法,传入注入的Bean名称;
  4. 如果Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader方法,传入类加载器;
  5. 如果Bean实现了BeanFactoryAware接口,调用setBeanFactory方法;
  6. 调用Bean后置处理器(BeanPostProcessor)的postProcessBeforeInitialization方法,特别留意一下:applyBeanPostProcessorsBeforeInstantiation这个后置处理器中会处理@PostConstruct注解;
  7. 如果Bean实现了InitializingBean接口,调用afterPropertesSet方法;
  8. 如果Bean实现了init-Method方法,调用Bean的init-Method方法;
  9. 调用BeanPostProcessor的postProcessAfterInitialization方法,这里执行完毕后,也表示这个Bean已经准备就绪,一直存在于上下文中直到被销毁;
  10. 如果应用上下文被销毁,如果Bean实现了DisposableBean接口,调用destroy方法,如果Bean中定义了destory-Method声明了销毁方法也会被调用;

3.1)FactoryBean

在生成Bean的过程中,首先判断是否满足标准,如果当前是一个FactoryBean,那么会把当前的Bean注册为一个名为:“&”+Bean名称的Bean定义,同时还会调用FactoryBean的getObject方法来获取返回的Bean并注册;

3.2)FactoryBean与BeanFactory

BeanFactory:是Spring顶层的核心接口,使用了简单工厂模式,仅仅只负责生产Bean; FactoryBean:是一个特殊的Bean,专门用来修饰我们的Bean,如果实现了这个接口,那么最终在处理的时候是通过FactoryBean中的方法进行获取的;

通俗的说,FactoryBean可以实现一个手动创建Bean的功能;而BeanFactory是由Spring来完成对Bean的初始化、属性填充、初始化工作;

在获取对象的时候可以可以通过在名称前加入&的方式获取原始的对象;

3.3)BeanPostProcessor

是Spring生态的一个体现,这个是一个拓展接口,用于扩展Spring,在Spring将Bean定义(BeanDefinition)转换为Bean的时候每一步都会调用不同的BeanPostProcessor实现,这样给足了开发人员拓展空间,从而让Spring生态更加强大;