Spring 的 bean 初始化过程

Spring 的 bean 初始化过程

前言

Spring 框架最基础来说, 是一个 DI(依赖注入) 框架. 我们把我们的程序中用到的实体对象, 也就是我们常说的 bean, 放在 Spring 框架中维护, 当我们用到这些 bean 的时候, 可以在程序的任何地方来获取.

Spring 封装了复杂的逻辑, 留给了我们很多便利又简单的方式来使用, 这正是我们使用 spring 框架的原因. 但是这也带来了一些问题, 我们对内部几乎一无所知, 当遇到复杂情况或者问题的时候, 往往无从下手. 而且, 随着 spring 很多自动化配置的出现, 和我们印象中的一些配置方式产生了冲突或重复, 即使在通常情况下能够启动运行, 我们也不敢说我们的配置绝对正确没有问题. 这个时候, 我觉得我们应该多了解一些内部实现, 这对于我们排查异常问题, 检查项目正确性都很有帮助.

那么我们在使用时, 就涉及到两个方面:

  1. 定义和注册 bean
  2. 获取 bean

我们从这两个方面来讲.

定义和注册 bean

我们在使用 spring 的时候, 有这么几种常用的定义 bean 的方式:

  1. xml 中来定义 bean 标签
  2. 通过注解扫描, 比如 @Service 这种类.
  3. 定义 Configuration 类, 在类中提供 @Bean 的方法来定义.
  4. 使用纯粹的 programmatically 的方式来定义和注册.

前三种方式, 我们经常使用, 我们简单看一下第四种方式是什么样子的

// 新建一个工厂
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

// 新建一个 bean definition
GenericBeanDefinition beanDefinition = (GenericBeanDefinition) BeanDefinitionBuilder
  .genericBeanDefinition(SomeService.class)
  .setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE)
  .getBeanDefinition();

// 注册到工厂
factory.registerBeanDefinition("someService", beanDefinition);

// 自己定义一个 bean post processor. 作用是在 bean 初始化之后, 判断这个 bean 如果实现了 ApplicationContextAware 接口, 就把 context 注册进去..(先不要管 context 哪来的...例子嘛)
factory.addBeanPostProcessor(new BeanPostProcessor() {
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    return bean;
  }

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (bean instanceof ApplicationContextAware) {
      GenericApplicationContext context = new GenericApplicationContext(factory);
      ((ApplicationContextAware) bean).setApplicationContext(context);
    }
    return bean;
  }
});

// 再注册一个 bean post processor: AutowiredAnnotationBeanPostProcessor. 作用是搜索这个 bean 中的 @Autowired 注解, 生成注入依赖的信息.
AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
autowiredAnnotationBeanPostProcessor.setBeanFactory(factory);
factory.addBeanPostProcessor(autowiredAnnotationBeanPostProcessor);

// getBean() 时, 初始化
SomeService SomeService = factory.getBean("someService",SomeService.class);
SomeService.doSomething();

我们定义了一个 BeanDefinition, 注册到了一个 BeanFactory 中; 然后从 BeanFactory 中获得这个 bean, 调用这个 bean 当中的方法.

事实上, 定义 BeanDefinition 并注册到 BeanFactory 的这个过程, 就是 bean 的定义和注册 , 我们通常使用 spring 时并不是把一个 bean 的实例注册, 而是一个 BeanDefinition.

而在第一次从 BeanFactory 当中 getBean() 时, 这个 bean 的实例才会真正生成. 这个部分我们在第二部分讲.

BeanFactory

BeanFactory 是专门用来获取 bean 的接口. 他又诸多的实现, 其中 ConfigurableListableBeanFactory 是最常用的.

另外 Context 类也实现了 BeanFacotry 的接口, Context 是通过继承和组合的方式对 BeanFactory 的一层封装, 避免直接访问到 BeanFactroy, 保证运行时不能随意对 BeanFactroy进行修改. 除此之外还提供了更多的功能: 它不仅有 BeanFactory 管理 bean 池的功能, 还要负责环境配置管理, 生命周期管理, 复杂的初始化操作等.

BeanFactory 的接口成层次比较复杂, 我总结了下主要的有以下功能的接口

  1. BeanFactroy

    最基础的 BeanFactory, 提供了根据 name , type 等获取一个 bean 对象的能力

  2. ListableBeanFactory

    继承 1, 额外提供了列出所有bean对象的能力

  3. HierarchialBeanFacotry

    继承 1, 额外使了 factory 拥有一个 parent factory 的能力, 可以存在层级关系

  4. singletonbeanRegistry

    提供了单例对象缓存的能力

  5. AutowireCapableBeanFacory

    继承 1, 提供了在创建对象时, 通过set方式给 bean 初始化 autowired 或者声明了 dependon 的成员

  6. ConfigurableBeanFactory

    继承 2 和 3, 给 factory 提供了很多配置的方法, 比如设置 BeanPostProcessor

  7. AliasRegistry

    支持了多个别名

最终经过各种组合, 我们一般用到实现类就两个

  1. StaticListableBeanFactory

    实现 ListableBeanFactory, 是最简单的 Beanfactory. 直接注册 bean 实例到 factory 中的 map 中, 获取时直接从 map 中返回. 如果我们自己写的简单项目需要做基本的依赖注入管理, 可以使用这一个.

  2. DefaultListableBeanFactory

    我们平常项目用到的, 通过 BeanDefinition 定义类, 在第一次 get 时实例化, 并在实例化过程中同时实例化依赖类, 并做属性填充, 并执行一些初始化前后的 processor.

    我们平常从 xml 中, 代码注解中定义的 bean, 通过 XmlBeanDefinitionReader 和 ClassPathBeanDefinitionScanner 生成 bean definition 注册到 DefaultListableBeanFactory中.

我们就从 DefaultListableBeanFactory 来讲初始化的过程, 如果想通过这个类来创建 bean , 就需要通过 xml配置, bean配置的方式生成 bean definition, 这是个怎样的东西呢.

BeanDefinition

完全的定义了一个 bean 的实例化, 初始化方式.

我们可能会关心的内部属性

parentName: String // bean 的名字

beanClassName: String // bean 类型名字

scope: String // 作用域, 默认的有 singleton 和 proto, 前者是我们常用的单例, 后者是每次新建一个 bean. 子类可以实现更多的scope, 比如 session, request.

lazyInit: boolean // 是否需要懒加载, 通常是由有context的应用来控制.

dependsOn: String[] // 依赖的 bean name, 跟是否要注入没关系, 有时是因为要控制 bean 的初始化顺序

autowireCandidate: boolean // 是否可以被 autowire, 默认true

primary: boolean // autowire 的优先级, 如果有多个 candidate, 会选 primary

factoryBeanName: String // 如果实例需要由工厂创建, 工厂 bean 的 name.

factoryMethodName: String // 工厂 bean 的 类型.

constructorArgumentValues: ConstructorArgumentValues // 构造函数参数表

propertyValues: MutablePropertyValues // 属性表

singleton: boolean 

prototype: boolean

abstract: boolean

role: int // bean 的定义者, 0: 由application定义, 即用户定义的. 1: support, 框架支持模块. 2: infrastructure, 框架的基础组件

description: String

resourceDescription: String // 定义来源

originatingBeanDefinition: BeanDefinition // 加工后的 BeanDefinition 保存原始 BeanDefinition.

beanClass: Class<?> // load bean 的 class 之后, 把 class 类型保存这里

abstract: boolean

autowireMode: int // 可以被autowire按类型, 按名称等

resolvedAutowireMode: int // 解决自己autowire属性时用按类型注入, 还是按构造参数传入

dependencyCheck: int // 依赖 check 策略, 全都 check, 还是只 check 引用类型, 还是只 check 简单类型, 或者不 check

initMethodName: String // 自定义的初始化方法

destroyMethodName: String // 销毁方法

synthetic: boolean // true 表示不需要多余的 实例化,初始化前后处理.

resource: Resource // 这个 bean definition 的 resource 引用

Bean的实例化过程

大概流程及说明, 以 singleton scope 的 bean 为例

  1. get bean from singleton cache

    首先从 singleton cache 获取对象, 如果有, 说明初始化过, 直接返回, 如果没有, 继续

    1. if return bean, return

    2. if null, go on

  2. create merged definition

    找到 bean 的 definition (bd), 然后生成 merged bean definition (mbd). 这个主要是因为 bd 可以有 parent, 这个步骤的作用就是把 bd 和 它的 parent bd (如果有的话), 进行merge, 生成 mbd, 之后就要根据这个 mbd 来实例化和初始化 bean.

  3. check bean definition

    检查 mbd , 把 mbd 中 dependsOn 的 bean 都先初始化.

  4. get singleton, if not found, then create bean:

    再次从 singleton cache 中获取 bean, 跟第 1 步方法参数不同, 这一步如果没有, 则会真正 create bean

    1. resolve class

      查找并 load 这个 bean 的 class

    2. resolveBeforeInstantiation

      在真正的实例化之前进行一次预先操作, 目的是给用户一个机会来进行非正常的实例化, 用户注入的 InstantiationAwareBeanPostProcessor 子类, 可以做一些 proxy, mock, 来取代真实的实例化返回, 如果没有产生 bean, 则继续往下走去正常的实例化阶段.

      1. InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
        1. if return bean, BeanPostProcessor.postProcessAfterInitialization(), then return bean.
        2. if return null, go on
    3. do create bean

      1. create instance

        真正的实例化, 调用 mbd 中定义的 factoryMethod, 或者类的构造方法, 来生成对象.

      2. MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition // 比如autowire等 注解注入 mbd

        对 mbd 进行初始化前的操作, 比如扫描 class 定义, 找到 @autowired 注解来生成注入信息存放在 mbd 中.

      3. add early singleton cache

        这一步主要是为了解决循环引用, 再把未初始化的 bean 的 reference 提供出来.

      4. populate bean

        填充属性阶段, properties values (pvs), 这一大步骤中的前三小步都是在构造 pvs, 并在最后一步 apply 进去

        1. InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()

        2. autowire

          把能够 set 的的属性写入 pvs

        3. InstantiationAwareBeanPostProcessor.postProcessPropertyValues()

          把一些特殊属性写入, 比如没有 set 的 autowired 属性, 一些 @Value 的属性

        4. apply property values

      5. initialize bean

        实例化完毕之后, 进行初始化.

        1. aware beanName, classLoader, beanFactory

          如果有需要, 把这三个特殊的对象放到 bean 里

        2. BeanPostProcessor.postProcessBeforeInitialization()

        3. init

          真正的初始化操作

          1. 如果 bean 是 InitializingBean, afterPropertiesSet()
          2. 调用自定义 init
        4. BeanPostProcessor.postProcessAfterInitialization()

      6. register disposable bean

我们在这个过程中能做的

  1. InstantiationAwareBeanPostProcessor

    实现这个接口的类, 可以在类的实例化阶段, 做一些操作.

    这个接口有三个方法:

    1. Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)

      在实例化之前进行一些操作, 目的是提供一个机会由这个方法来实例化对象. 比如 proxy, mock

    2. boolean postProcessAfterInstantiation(Object bean, String beanName)

      bean set属性前执行, 如果返回false, 将会跳过属性处理. 这个一般都是返回true.

    3. PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)

      属性构造完成, apply 到对象前执行, 传入 pvs 并返回 pvs, 有机会对 pvs 进行处理. 比如 required 检测在这一步.

  2. BeanPostProcessor

    这个接口有两个方法:

    1. Object postProcessBeforeInitialization(Object bean, String beanName)

      在初始化之前 传入 bean 并返回 bean, 但返回的 bean 可以被包装或者替换

    2. Object postProcessAfterInitialization(Object bean, String beanName)

      同上, 也可以做一些 bean 初始化完成后的回调. 比如可以监听每一个 bean 的初始化时机.

交流

欢迎加入群 661035226,gradle,spring,activiti 交流

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容