Spring的Bean生命周期

Spring Bean的生命周期指的是从一个普通的Java类变成Bean的过程:

image.png

以注解类变成Spring Bean为例,Spring会扫描指定包下面的Java类,然后将其变成beanDefinition对象,然后Spring会根据beanDefinition来创建bean。特别要记住一点,Spring是根据beanDefinition来创建Spring bean的,关于beanDefinition下文会进行分析。
image.png

上图就是beanDefinition所包含的内容,看到这些属性如果对Spring有所了解都应该知道几个,如lazyInit懒加载,在Spring中有一个@Lazy注解,用来标识其这个bean是否为懒加载的,scope属性,相应的也有@scope注解来标识这个bean其作用域,是单例还是多例,beanClass属性,在对象实例化时,会根据beanClass来创建对象、又比如autowireMode注入模型这个属性,这个属性用于记录使用怎样的注入模型,注入模型常用的有根据名称和根据类型、不注入三种注入模型。

autowireMode虽然看似我们没有使用到过,但是知道这个对于查看Spring和其他框架整合的时候很有帮助,如果我们在beanDefinition指定其根据类型进行属性注入,那么在创建这个bean时,会将其beanClass中的所有属性都拿到,然后排出掉基本属性(如类型String、Double、Boolean、Object),然后对于剩下的进行属性注入,记住一点,在beanDefinition指定其根据类型进行属性注入,即使不在属性上面使用@Autowired注解也会对其进行属性注入。在我们写注解类的时候为什么不使用@Autowired时,其属性就注入不进来呢?那是因为注解类在变成beanDefinition时,其注入类型是不注入,所以此时只有使用@Autowired注解进行标记的属性,才会完成依赖注入。

Spring会根据beanDefinition来完成bean的创建,为什么不直接使用对象的class对象来创建bean呢?因为在class对象仅仅能描述一个对象的创建,它不足以用来描述一个Spring bean,而对于是否为懒加载、是否是首要的、初始化方法是哪个、销毁方法是哪个,这个Spring中特有的属性在class对象中并没有,所有Spring就定义了beanDefinition来完成bean的创建。

java类 -> beanDefinition对象
Java类是在哪一步变成beanDefinition的,我们先来看一下Spring中AbstractApplicationContext类中的refresh方法:

  public void refresh() throws BeansException, IllegalStateException {
    synchronized(this.startupShutdownMonitor) {
      StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
      this.prepareRefresh();
      ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
      this.prepareBeanFactory(beanFactory);

      try {
        this.postProcessBeanFactory(beanFactory);
        StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
        this.invokeBeanFactoryPostProcessors(beanFactory);
        this.registerBeanPostProcessors(beanFactory);
        beanPostProcess.end();
        this.initMessageSource();
        this.initApplicationEventMulticaster();
        this.onRefresh();
        this.registerListeners();
        this.finishBeanFactoryInitialization(beanFactory);
        this.finishRefresh();
      } catch (BeansException var10) {
        if (this.logger.isWarnEnabled()) {
          this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
        }

        this.destroyBeans();
        this.cancelRefresh(var10);
        throw var10;
      } finally {
        this.resetCommonCaches();
        contextRefresh.end();
      }

    }
  }

step1:prepareRefresh 上下文刷新前准备工作:设置ConfigurableWebApplicationContext上下文实例对象wac的启动日期和活动标志、加载属性源配置以及判断必填项是否都完整。

step2:obtainFreshBeanFactory 通知子类刷新内部bean工厂工作:创建BeanFactory,如果已有就销毁,没有就创建;核心工作就是解析XML 以及扫描注解,将扫描到的Bean配置属性封装到BeanDefinition 对象中,并对它beanName(key) , BeanDefinition(v) 保存到一个Map 中

image.png

step3:prepareBeanFactory 对bean factory进行一些上下文标准化配置:设置factory的类加载器、bean 表达式解释器、资源编辑注册器、应用上下文自动注入后处理器、配置在自动装配(通过beans标签default-autowire属性来依赖注入)的时候的需要忽略的类(如ApplicationContextAwareProcessor、EnvironmentAware、ResourceLoaderAware、 EmbeddedValueResolverAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware)、注册可以自动装配的 Bean(如BeanFactory)以及注册默认系统变量配置等。

image.png

step4:postProcessBeanFactory 修改BeanFactory的后处理器配置:在标准初始化之后修改应用程序上下文的内部bean工厂初始化配置,允许注册特殊BeanPostProcessors->即ServletContextAwareProcessor 后处理器以及设置自动装配忽略接口类(ServletContextAware、ServletConfigAware)以及注册应用上下文的request/session 范围。 这一阶段所有bean定义都已加载,但没有bean将被实例化。

step5:invokeBeanFactoryPostProcessors 工作:通过显示顺序方式调用手动注册的BeanFactory后处理器,先实例化spring框架涉及到的后处理器,在调用。

(1)先执行 BeanDefinitionRegistryPostProcessor 的方法
(2)对配置/组件类中声明的嵌套@component类或configuration类者进行自动注册成bean定义,并对这些候选bean定义按照优先级进行排序。
(3)执行beanFactoryPostProcessors,对配置文件bean进行扫描实例化:如propertyConfigurer。


image.png

image.png

image.png

step6:registerBeanPostProcessors 注册拦截bean创建的bean处理器-实例化:
(1):通过beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);方法获取beanFactory里继承了BeanPostProcessor接口的name的集合;
(2):把后置器beans分为PriorityOrdered、Ordered、nonOrdered三大类,前两类是增加了排序条件的后置器;
(3):前两类后置器执行sortPostProcessors和registerBeanPostProcessors方法,也就是先执行排序方法,后执行注册方法。
(4):最后一步用到了上面提到的BeanPostProcessor和BeanFactoryPostProcessor的入参不同的AbstractApplicationContext,在addBeanPostProcessor方法里把BeanPostProcessor注册进了AbstractBeanFactory,这也就是为什么BeanFactoryPostProcessor执行了后置接口实现类,而BeanPostProcessor仅仅执行了注册和实例化,而没有执行的原因。

step7:initMessageSource 初始化消息源:MessageSource接口类用于支持信息的国际化和包含参数的信息的替换。
ApplicationContext接口继承了MessageSource接口,应用可通过ApplicationContext来调用MessageSource接口方法以实现信息的国际化和替换信息中包含的参数。所有对MessageSource接口的实现都是在AbstractApplicationContext中实现。

step8: initApplicationEventMulticaster 初始化事件广播器:如果上下文中没有定义则使用默认广播器:SimpleApplicationEventMulticaster。

step9: onRefresh 初始化其他特定的bean,由具体子类实现。

step10:registerListeners 注册监听事件:在容器中将所有项目里面的ApplicationListener注册进来,大体过程如下:获取所有的事件,并添加到事件派发器中 -> 监听事件进行派发广播。

step11:finishBeanFactoryInitialization 初始化所有剩下的单实例 Bean(没有配置赖加载的 lazy!=true)。大体过程如下:

-> 获取bean的定义信息
-> 判断bean 是否是抽象的、是单例的、非懒加载的
-> 是否为 FactoryBean ,是则调用 FactoryBean 的创建方法,否则执行 getBean() 方法
-> 调用 getBean() 方法
-> getBean方法内部再调用 doGetBean() 方法

step12:finishRefresh 完成BeanFactory的初始化创建工作:
// 初始化生命周期处理器组件
initLifecycleProcessor();

// 首先将刷新状态传播到生命周期组件中.
getLifecycleProcessor().onRefresh();

// 发布上下文已刷新完毕的事件.
publishEvent(new ContextRefreshedEvent(this));

// Participate in LiveBeansView MBean, if active:
LiveBeansView活动beans的查看适配器,构建当前bean的快照 和来自本地{@code ApplicationContext}的依赖关系(带有本地{@code LiveBeansView} bean定义)或所有已注册的ApplicationContexts由{@value #MBEAN_DOMAIN_PROPERTY_NAME}环境属性驱动)
LiveBeansView.registerApplicationContext(this);

doGetBean方法处理过程

1)获取bean所对应clazz的构造函数
(2)构造函数先执行静态字段的初始化,然后按照属性字段声明顺序执行初始化
(3)调用各个MergedBeanDefinitionPostProcessor后处理器的postProcessMergedBeanDefinition
    进行信息合并处理。
(4)在容器中为bean 添加一个单指定的单例工厂
(5)调用各个InstantiationAwareBeanPostProcessor后处理器的postProcessAfterInstantiation
(6)调用autowireByName方法处理通过autowire设置为byName的属性字段: xml 中bean的autowire指定
(7)调用autowireByType方法处理通过autowire设置为byType的属性字段 :xml 中bean的autowire指定
(8)若有实例化感知后处理器InstantiationAwareBeanPostProcessor,则调用postProcessPropertyValues
        进行处理:更新beanFactory的设置
(9)若有bean属性值依赖检查,则进行检查:依赖性检查属性可以是协作对象bean,简单类型或全部
(10)调用applyPropertyValues方法对MutablePropertyValues可变属性-复杂对象进行设值,涉及到属性值
    依赖注入,主要是xml中的bean配置, 例如PropertyPlaceholderConfigurer::propertyConfigurer配置,
     <bean id="propertyConfigurer" 
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
         <property name="ignoreUnresolvablePlaceholders" value="true"/>
        <property name="properties" ref="propertiesFactoryBean"/>
     </bean>
     
     <bean id="propertiesFactoryBean" 
      class="org.springframework.beans.factory.config.PropertiesFactoryBean">
            <property name="ignoreResourceNotFound" value="true"/>
            <property name="locations">
              <list>
                <value>classpath*:production.properties</value>
             </list>
           </property>
    </bean>
    在这一阶段会解析property配置文件。
(11)调用bean初始化之前的后处理器的postProcessBeforeInitialization方法对初始化(@postContruct、@preDestroy标注方法)方法进行处理调用
(12)调用初始化方法afterPropertiesSet():bean是实现了InitializingBean
(13)调用bean初始化之后的后处理器,一般是没有操作,直接返回。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容