SpringBoot深入学习(一)-- refresh()

  springboot在启动的时候,会调用run方法,创建环境设置spring容器,其中包含refresh方法,完成配置类解析,各种beanFactoryPostProcessbeanPostProcessor注册,web内置容器构造,国际化配置初始化等,refresh调用了父类AbstractApplicationContextrefresh方法如下。

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
    // 准备,记录容器的启动时间startupDate, 标记容器为激活,初始化上下文环境如文件路径信息,验证必填属性是否填写 
    prepareRefresh();

    // 这步比较重要(解析),告诉子类去刷新bean工厂,这步完成后配置文件就解析成一个个bean定义,注册到BeanFactory(但是未被初始化,仅将信息写到了beanDefination的map中)
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 
    // 设置beanFactory类加载器,添加多个beanPostProcesser
    prepareBeanFactory(beanFactory);
 
    try {
        // 允许子类上下文中对beanFactory做后期处理
        postProcessBeanFactory(beanFactory);
 
        // 调用BeanFactoryPostProcessor各个实现类的方法
        invokeBeanFactoryPostProcessors(beanFactory);
 
        // 注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别
         // 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
         // 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
        registerBeanPostProcessors(beanFactory);
 
        //初始化ApplicationContext的MessageSource
        initMessageSource();
 
        //初始化ApplicationContext事件广播器
        initApplicationEventMulticaster();
 
        // 初始化子类特殊bean(钩子方法)
        onRefresh();
 
        // 注册事件监听器
        registerListeners();
 
        // 初始化所有singleton bean  重点!!重点!!
        finishBeanFactoryInitialization(beanFactory);
 
        // 广播事件,ApplicationContext初始化完成
        finishRefresh();
} catch (BeansException ex) {
....................
}
image

1. prepareRefresh()

  在rehresh之前做的准备工作,一是设置spring启动事件,开启活跃状态;二是初始化属性源信息;三是验证必要属性。

image

1.1 initPropertySources()

  在应用启动之前替换一些属性占位符,这个方法再Spring的对象中一般都没有实现,应该是用来方便我们后期扩展使用的方法。

1.2 validateRequiredProperties()

  Environment类的方法验证必须的属性是否正确。

2. obtainFreshBeanFactory();

  获取新的beanFactory,销毁原有beanFactory、为每个bean生成BeanDefinition
  obtainFreshBeanFactory方法从字面的意思看获取新的Bean工厂,实际上这是一个过程,一个加载Xml资源并解析,根据解析结果组装BeanDefinitions,然后初始化BeanFactory的过程。如下图跳入方法源码中

image

2.1 refreshBeanFactory()

  refreshBeanFactory是具体的刷新BeanFactory,负责这个工作做在类AbstractRefreshableApplicationContext中,顾名思义这是专门用来刷新的。

image

详细说明:

  1. 首先判断,是否存在BeanFactory,如果存在容器beanFactory,则先销毁所有的bean,然后关闭beanFactory
  2. DefaultListableBeanFactory beanFactory = createBeanFactory(); 创建初始容器beanFactory,此处创建的是DefaultListableBaenFactory,是最重要的beanFactory,即初始化容器;spring注册&加载bean的基本容器;
    image
  3. customizeBeanFactory(beanFactory);根据AbstractRefreshableApplicationContext类的属性为Beanfactory设置属性值。
    image

    allowBeanDefinitionOverriding:属性是指是否允对一个名字相同但definition不同进行重新注册,默认是true。 allowCircularReferences属性是指是否允许Bean之间循环引用,默认是true
    默认两个属性都为空,可扩展的,可以自己设置属性,方法就是继承ClassPathXmlApplicationContext并复写customizeBeanFactory方法为两个属性设置值即可。
  4. loadBeanDefinitions(beanFactory);BeanDenifition注册到bean工厂
    [图片上传失败...(image-e0509-1560845367203)]查看最后一个方法:
    loadBeanDefinitions(beanDefinitionReader);
    image

    例如:applicationContext.xml不是在calsspath下了,只有Resource,那怎么办,直接传递进来即可,继承ClassPathXmlApplicationContext类重写getConfigResources方法,返回Resource即可。

3. prepareBeanFactory();

  在ApplicationContext刷新获取beanFactory之后,开始准备context使用的beanFactory。这一步相对比较简单,就是配置beanFacotry的特性

  1. beanFactory.setBeanClassLoader(getClassLoader());设置类加载器;设置BeanFactoryBeanClassLoader,如果存在,则直接使用之前的那个,否则,初始化一个新的ClassLoader;

    image

  2. beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));设置BeanExpressionResolver表达式解析器,主要用来解析EL表达式;Bean初始化完成后填充属性时会用到

  3. beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));设置属性注册解析器PropertyEditor;这个主要是对某些注入的Bean的一些属性的支持;

  4. beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor (this));此处添加了一个BeanPostProcessor实现:ApplicationContextAwareProcessor,它是用来处理并回调实现了各种Aware接口的Bean,比如获取ApplicationContext的ApplicationContextAware接口。添加BeanPostProcessor(Bean后置处理器ApplicationContextAwareProcessor),当bean被这个工厂创建的时候会用到PostProcessor, 在beanfactory中存储了beanPostProcessors的列表,在生效的时候,列表中的PostProcessor都会执行。 在bean初始化之前,调用ApplicationContextAwareProcessorpostProcessBeforeInitialization处理所有的Aware接口,进行如下操作:

    // 如果bean实现了EmbeddedValueResolverAware接口,调用bean.setEmbeddedValueResolver
    // 如果bean实现了ResourceLoaderAware接口,调用bean.setResourceLoader
    // 如果bean实现了ApplicationEventPublisherAware接口,调用bean.setApplicationEventPublisher
    // 如果bean实现了MessageSourceAware接口,调用bean.setMessageSource
    // 如果bean实现了ApplicationContextAware接口,调用bean.setApplicationContext
  1. beanFactory.ignoreDependencyInterface()指定的接口不会被自动注入进去。
  2. beanFactory.registerResolvableDependency()设置几个自动装配规则,例如BeanFactory则注入beanFactory ResourceLoader,ApplicationEventPublisher,ApplicationContext注入当前对象
    image
  3. beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)如果当前BeanFactory包含loadTimeWeaver Bean,说明存在类加载期织入AspectJ,则把当前BeanFactory交给类加载期BeanPostProcessor实现类LoadTimeWeaverAwareProcessor来处理,从而实现类加载期织入AspectJ的目的。
    image
  4. 注册当前容器环境environment组件Bean
    [图片上传失败...(image-edc8e7-1560845367203)]
  5. 注册系统配置systemProperties组件Bean
    image
  6. 注册系统环境systemEnvironment组件Bean
    image

  总结:
(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()

  bean工厂的bean属性处理容器,说通俗一些就是可以管理我们的bean工厂内所有的beandefinition(未实例化)数据;
  postProcessBeanFactory后处理beanFactory。时机是在所有的beanDenifition加载完成之后,bean实例化之前执行。
  比如,在beanfactory加载完成所有的bean后,想修改其中某个bean的定义,或者对beanFactory做一些其他的配置,就可以用此方法。在系列文章中,可以实现ClassPathXmlApplicationContext类并重写postProcessBeanFactory即可。

默认情况下此方法是空的。需要子类去实现
注意:
BeanFactoryPostProcessor的主要作用是让你能接触到bean definitions,对bean definitions进行一定hack,但是也仅此而已了。绝对不允许在BeanFactoryPostProcessor中触发到bean的实例化!
举例:

@Component
public class PrematureBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        Map<String, BBean> map = beanFactory.getBeansOfType(BBean.class);
        for (BBean bBean : map.values()) {
            assert bBean.getABean() == null;
        }
    }
}

@Component("bBean")
public class BBean {

    @Autowired
    private ABean aBean;

    public ABean getABean() {
        return aBean;
    }

}

@Component
public class ABean {

    private String name = "a";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

  在运行后,BBean中被期待注入的ABean最终为null。这是为啥呢?postProcessBeanFactory(beanFactory); 首先invoke了容器中的BeanFactoryPostProcessor实现类,其中当然就包括PrematureBeanFactoryPostProcessor,此时通过beanFactory.getBeansOfType触发了bean提前实例化。按理说,bean提前实例化也应该没问题的,aBean也应该是能够被注入的呀!那为啥最终不是这个结果呢?
  从源码可知:
@AutoWired起作用依赖AutowiredAnnotationBeanPostProcessor
@Resource依赖CommonAnnotationBeanPostProcessor,这俩都是BeanPostProcessor的实现。
  那BeanPostProcessors在何处被spring invoke呢?
参见refresh()方法registerBeanPostProcessors(beanFactory);
postProcessBeanFactory(beanFactory);后面被调用;
  也就是说BBean被触发提前初始化的时候,AutowiredAnnotationBeanPostProcessor还没有被注册自然也不会被执行到,自然ABean=null

5.invokeBeanFactoryPostProcessors(beanFactory);

  实例化Factory之前预处理部分
  invokeBeanFactoryPostProcessors 方法主要用于处理 BeanFactoryPostProcessor 接口。BeanFactoryPostProcessor 接口是 Spring 初始化 Bean 时对外暴露的扩展点,Spring IoC 容器允许 BeanFactoryPostProcessor 在容器实例化任何 Bean 之前读取Bean 的定义,并可以修改它。
  可以理解为,初始化我们自定义的BeanFactoryPostProcessor
postProcessBeanFactory用于在标准的初始化完成后修改容器上下文中的beanFactory。所有bean定义将被加载,但是它们将暂时不被实例化,这允许覆盖,甚至添加一些属性到延迟初始化的bean上。也就是说,允许我们开发者自定义的去修改BeanFactory中的内容,这也是符合“spring”的开闭原则。invokeBeanFactoryPostProcessors,就是调用postProcessBeanFactory

image

5.1. getBeanFactoryPostProcessors()

  获取的手动注册的BeanFactoryPostProcessor;
  是通过beanFactory.addBeanPostProcessor()方法设置的BeanFactoryPostProcessor。默认空的,因为没有通过addBeanFactoryPostProcessor方法添加BeanFactoryPostProcessor

image

5.2 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()

未完待续...

6. registerBeanPostProcessors(beanFactory);

注册BeanPostProcessor
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

image

image

注册BeanPostProcessorinvokeBeanFactoryPostProcessors很像,并且没什么复杂逻辑。就是将已经注册到beanFacory的Bean筛选出BeanPostProcessor然后添加到ApplicationContextbeanPostProcessor集合中。
主要逻辑:

  1. :首先在容器中注册一个BeanPostProcessorChecker,这个只是用来对在bean不适合所有的BeanPostProcessor调用的情况下,打印一些日志信息。

  2. :先注册实现了PriorityOrdered接口的BeanPostProcessor,再注册实现了Ordered接口的的BeanPostProcessor,然后注册什么接口都没实现的BeanPostProcessor,最后注册实现了MergedBeanDefinitionPostProcessor接口的BeanPostProcessor

  3. :注册ApplicationListenerDetector。用来检测bean是否是ApplicationListener,如果是判断是否是单例,如果不是单例,那么删除singtonNames中对应的key

7. initMessageSource();初始化消息源

为本context初始化消息源;
判断beanFactory中是否有名字为messageSource的bean,如果有,从beanFactory中获取并且判断获取的是不是HierarchicalMessageSource类型的,如果是设置其父级消息源;如果没有,新建DelegatingMessageSource类作为messageSource的Bean。

image

8. initApplicationEventMulticaster();初始化应用事件广播器

初始化ApplicationEventMulticaster
首先判断beanFactory容器中是否存在这个广播器:applicationEventMulticaster
如果存在,则用beanFactory中的bean;如果不存在则使用新建SimpleApplicationEventMulticaster

image

9. onRefresh();

10. registerListeners();注册监听器

在发布事件的时候会从这里注册的监听器中去获取;


image

11.finishBeanFactoryInitialization(beanFactory);

完成此上下文的 bean 工厂的初始化,初始化所有剩余的单例 bean。实例化所有的非懒加载单例。是ApplicationContext刷新的时候,最重要的方法了,因为所有的bean,如果不是lazy-init的都会在这一步进行实例化,并且做一些处理。

image

12.finishRefresh();

完成对context的刷新

13.总结

1.prepareRefresh();对刷新进行准备,包括设置开始时间,设置激活状态,初始化Context中的占位符,子类根据其需求执行具体准备工作,而后再由父类验证必要参数

2.obtianFreshBeanFactory();,刷新并获取内部的BeanFactory对象

3.prepareBeanFactory(beanFactory);,对BeanFactory进行准备工作,包括设置类加载器和后置处理器,配置不能自动装配的类型,注册默认的环境Bean

4.postProcessBeanFactory(beanFactory);为Context的子类提供后置处理BeanFactory的扩展能力,如想在bean定义加载完成后,开始初始化上下文之前进行逻辑操作,可重写这个方法

5.invokeBeanFactoryPostProcessors(beanFactory);,执行Context中注册的BeanFactory后置处理器,有两张处理器,一种是可以注册Bean的后置处理器,一种的针对BeanFactory的后置处理器,执行顺序是先按优先级执行注册Bean的后置处理器,而后再按优先级执行针对BeanFactory的后置处理器

SpringBoot中会进行注解Bean的解析,由ConfigurationClassPostProcessor触发,由ClassPathDefinitionScanner解析,并注册到BeanFactory

6.registerBeanFactoryProcessor(beanFactory();,按优先级顺序在BeanFactory中注册Bean的后置处理器,Bean处理器可在Bean的初始化前后处理
7.initMessageSource();初始化消息源,消息源用于支持消息的国际化

8.initApplicationEventMuticaster();初始化应用事件广播器,用于向ApplicationListener通知各种应用产生的事件,标准的观察者模型

9.onRefresh();,用于子类的扩展步骤,用于特定的Context子类初始化其他的Bean

10.registerListeners();,把实现了ApplicationListener的类注册到广播器,并对广播其中早期没有广播的事件进行通知

11.finishBeanFactoryInitialization(beanFactory);,冻结所有Bean描述信息的修改,实例化非延迟加载的单例Bean

12.finishRefresh();,完成上下文的刷新工作,调用LifecycleProcessor.onRefresh(),以及发布

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

推荐阅读更多精彩内容