抽丝剥茧spring源码(一)

当5G来临,当211高校已经开启人工智能课程,当甲骨文大批量裁员,大家的心是否像我一样为之一颤呢?当科技不断发展,技术迅速迭代,程序员愈发年轻化的今天,而作为我们已经步入中年的程序员来说路在何方?当我们逐渐老去,我们不能指望企业家的怜悯,当大批年轻化的程序员涌入互联网大潮时,他们的思维,他们的体能,甚至他们的能力都远超于我们,我们又该何去何从?职场不相信眼泪,更不会同情,唯有修炼内功,修炼职场硬实力,当然如果硬实力不行,也只能来点软的了......

作为程序员不懂高并发、JVM优化、系统内核、大数据、框架源码... ...,整天写CRUD,也许很快就会被时代所淘汰。

下面让我带你抽丝剥茧spring源码。会从spring ioc容器核心结构、spring ioc注入执行流程的脉络讲起,包括什么是BeanFactory和FactoryBean;什么是BeanDefinition及其有哪些重要的实现类;什么是BeanPostProcessor、BeanFactoryPostProcessor、ImportSelector、ImportBeanDefinitionRegistrar,以及如何应用他们来实现对spring的扩展等等,逐步深入细节,当然spring源码非常庞大难懂,本文是从spring ioc讲起,重点是梳理spring ioc注入的脉络,之后的博文会一点点抽丝剥茧的对spring内部处理细节做逐步分析讲解,包括Spring aop、spring Tx等等,来逐步解密spring。

一、带您初识spring容器模型

f9129f90f7d1885caacf97acfb50848064a.jpg

在学习spring ioc源码之前,有必要先了解下其核心类的含义及作用。上面是一个粗略的spring bean工厂内部存储,鉴于spring工厂比较庞大,上图也只画出了一部分比较重要的核心类,但是即便这样这个图片也已经看不清了,但是我会带您一个个讲解说明。

初始化spring容器测试代码:

3a31e7990cb9359fd48d147459a5b2e52a0.jpg

900b440c197cbe30d0940bb8ccf68f01294.jpg

    1、首先既然是spring ioc,它的最重要的作用就是管理bean,所以我们猜测肯定是会有一个bean工厂的,这个是很多框架都有的(如mybatis),上来就会来个工厂。这就是                     BeanFactory,在spring中默认的实现类为DefaultBeanFactory,DefaultBeanFactory作为bean工厂的默认实现,主要提供了bean的注册,bean的获取取。当然bean的注册和            获取的具体实现是由其子类AbstractBeanFactory和DefaultSingletonBeanRegistry类完成。

       其实在本文开始的时候我也提到了FactoryBean,那么FactoryBean又是什么的?它是一个spring提供的一个接口,其实大家目前可以简单粗暴的理解为是一个特殊的bean,在某些         情况下bean的实例化过程比较复杂,可以实现FactoryBean接口重写getObject()方法来实现一个复杂bean的实例化,实现FactoryBean接口会在spring工厂中注入2个bean,实现         类本身会对应一个bean,beanName为&+类名(首字母小写),还有一个就是getObject()返回的bean,也就是我们自己自定义的bean,beanName为返回的Object类的类名            (首字母小写)。如我们比较熟悉的mybatis就是应用了spring的这个FactoryBean这个扩展类,具体mybatis中的哪个类应用了,大家自己想吧。

     2、BeanDefinition:顾名思义,这个类是描述bean的接口。有两个重要的实现类分别是RootBeanDefinition和AnnotatedGenericBeanDefinition。RootBeanDefinition是用来定            义Spring内部的bean,如ConfigurationClassPostProcessor;AnnotatedGenericBeanDefinition是用来定义我们自定义的bean,也就是我们注入spring容器,需要spring管理            的bean。

     3、说完BeanDefinition,我们来认识下bean的元数据定义类。ClassMetadata:定义类的元数据,其中包括getClassName、isInterface、isAnnotation方法;                                          MethodMetadata:定义方法的元数据;AnnotationMetadata:定义注解类元数据信息;

     4、Spring初始化时bean是怎么存储的?答案是存储在DefaultListableBeanFactory类的集合中,其中BeanDefinitionMap是存储了beanName和BeanDefinition的映射,                          beanDefinitionNames存储了beanName,见下图:

        601cbb1497be3d616bc1f69ff96b2ca6cc9.jpg

        0592e42f676b660d3f3f2411ec8f21a8976.jpg

       

        但请注意此时bean还没有实例化,实例化后,bean是存储在DefaultSingletonBeanRegistry类中的singletonObjects中,这个我们在之后分析源码的时候会详细说。

        5、AnnotatedBeanDefinitionReader:这个类是spring容器初始化的时候,在DefaultListableBeanFactory初始化之后就会实例化的一个类。它主要的作用是向spring bean工厂中注册我们自定义的bean,但是这个类其实是委托了BeanDefinitionReaderUtils类去调用DefaultListableBeanFactory类去完成bean的注册的,可见DefaultListableBeanFactory的重要性。

        6、BeanDefinitionHolder:封装了beanName和BeanDefinition,不用关注。

        7、BeanFactoryPostProcessor:bean工厂的后置处理器,可以插手bean工厂初始化过程,实现这个接口会得到beanFactory实例,也就是可以操作bean工厂了。至于什么时候会执行其实现类,那是后面博文会讲解的内容。

        8、BeanPostProcessor:bean的后置处理器。会插手bean实例化的过程,实现此接口可以操纵容器中正在初始化的bean,也就是说可以对bean属性做修改。

       9、BeanDefinitionRegistryPostProcessor:是个接口,实现了BeanFactoryPostProcessor接口,定义了postProcessorBeanDefinitionRegistry(registry)方法,也就是扩展了BeanFactoryPostProcessor接口,spring内部ConfigurationClassPostProcessor类实现了这个接口,去完成bean的解析。

        10、实例化AnnotatedBeanDefinitionReader时,同时spring会向容器中注入7个spring自带的bean处理器。这些处理器在spring容器中都起着重要作用。这个也留待我们之后的博文中进行深入讲解。

        11、ClassPathBeanDefinitionScanner:主要用于包的扫描工作。后续博文中会详细说。

        以上简要带大家认识了spring ioc注入过程中涉及到的一些比较重要的spring核心类。

    

二、Spring ioc初始化流程

1f3cd9cd19827111dddfb8549f0bea09eb7.jpg

下面我们就以注解方式初始化spring为例讲解下spring容器初始化过程。

 

Spring注解方式启动:实例化AnnotationConfigApplicationContext,传入需要扫描的配置类或需要注入的beanclass。接着我们就来进入主题,看下源码入口:

403bf3c0b1b57322132e37216939785e594.jpg

2.1实例化父类构造方法

如上图,首先会执行其父类的构造方法,正如我这个方法上注释的说明,主要实例化三个类,我们分别看下:

GenericApplicationContext:主要实例化了beanFactory,默认为DefaultListableBeanFactory。

792b3d7aa36ff99c718b60dcb712d23b739.jpg

AbstractApplicationContext:主要实例化PathMatchingResourcePatternResolver。这个类我在第一部分没有拿出来说明,其实可以理解它是资源文件解析器,提供了解析资源文件的功能。

7c03ba859a8db8a8c4fc154b30d789e7bc3.jpg

 DefaultResourceLoader:实例化类加载器,其实就是APPClassLoader的实例。

fa9987ed617c412383d96410d3aedd48932.jpg

执行完父类构造方法后,我们看下spring容器中的变化,即向spring容器中实例化了:

a90607de12efc9576f89f3736b3f05afb3e.jpg

ce419ce8bd5f575f47d9b8ad17301735205.jpg

26fabc053025e05db11e504ef54036e919c.jpg

2.2无参构造方法

父类构造方法执行结束,我们就来看下主体方法中的第一个方法this()。它会调用无参的构造方法:

2b8f5cf5b199d4b4c5eb6b7490b2df3e642.jpg

通过上面对代码的注解,你应该大致了解了这个构造方法做了什么。下面就在来看下里面的实现。

3dfc4a0d120ba27703f71ebe5dd6634aabe.jpg

这个类最终主要会调用AnnotationConfigUtils.registerAnnotationConfigProcessors()方法,也就是AnnotatedBeanDefinitionReader会委托AnnotationConfigUtils类做一些事情,那么我们就看下registerAnnotationConfigProcessors()方法做了哪些事情。

82b78eadf1cb80324d7be2d0e442bcf55fc.jpg

f3a1c35de9459ad265a6ade0b038a5f9a5e.jpg

27c6af2f4dc14e4b9fcd046e3918cd69254.jpg

0d3ab6c3e5ed09eef8e1854789b7aef2386.jpg

正如这段代码中我注释所写的那样,主要是向bean工厂中注册spring内部自定义的bean。其中这些bean有的是实现了BeanPostProcessor、BeanFactoryPostProcessor接口的,这两个接口我们在第一部分中做过说明。被注册的6个bean在后续的操作中都有着各自的作用,这些类会在后续的博文中做详细的介绍。

这里每个bean都会封装成RootBeanDefinition(这个我们在第一部分也做过说明,RootBeanDefinition是封装spring内部自定义的bean的),最终会调用registry.registerBeanDefinition(beanName, definition)方法向容器中注册bean。Registry其实就是DefaultListableBeanFactory的实例,那么也就是调用DefaultListableBeanFactory类的registerBeanDefinition()方法:

77a7ab9070d52ed774812ab295c24f5087e.jpg

0049ef048f22b282d990c5c45a439757b33.jpg

上面是当前会执行的代码部分,也就是会将那6个bean都注册到beanDefinitionMap

中。这也就是实例化AnnotatedBeanDefinitionReader类主要做的事情。

我们在来看下现在spring容器中的变化,其实就是bean工厂中注册了7个spring自定义的bean:

b9e5e1716dc29a88f7e3fb284736bc0083a.jpg

078ea2bdeecad1ec9caffae0fcf5e7307dc.jpg

上面说了构造函数中的this.reader = new AnnotatedBeanDefinitionReader(this)方法,接下来说下构造函数中ClassPathBeanDefinitionScanner实例化的过程。

ef1268a2fd0be80a253b4e54ebab74365df.jpg

其实这个实例化的过程比较简单,除了实例化ClassPathBeanDefinitionScanner类之外,主要就是注册了几个注解:

bf000c73af83d4994c63af46448887cc124.jpg

暂时可以先不用管这部分,不重要。

此时就完成了spring容器初始化的第一步,再让我们来看下spring容器目前的情况:

f2f93937c07995ef9dea7ffebd79cca9fb7.jpg

5346ce68b0880d7b96ea17d85a17f2e65f6.jpg

3535f719a0426acee71f901011921339125.jpg

e0db2bfe0cff5b3318e15a20e46ff035746.jpg

2.3 register(annotatedClasses)注册自定义bean

接下来就要处理我们需要spring容器来管理的beanclass了。其实就是调用AnnotatedBeanDefinitionReader的register方法,其中参数AnnotatedClasses是我们传入的需要注入的bean,可以是一个带有@ComponentScan注解的配置类,也可以是一个bean集合,也可以是一个单个bean,所以此处做了循环操作处理。

35b5b46a3150b8345ad0eb645fa868200dc.jpg

最终会调用doRegisterBean()方法:

这个方法首先会调用shouldSkip()方法对beanclass做校验,具体校验逻辑见我对代码的注解说明:

b573a9bc80fab8a16edb967c8efbf4cce44.jpg

接着会调用AnnotationConfigUtils.processCommonDefinitionAnnotations()方法,在这个方法中会给这个bean实例赋予属性,主要是通过这个beanclass上的注解来赋值,会看是否的@Lazy的、@Primary的值、@DependsOn的值、@Role的值、@Description的值,beanclass上存在这些注解就会取值并赋给bean实例。具体实现我们在之后的博文中会详细分析。

a2b1c4223ac3f86151262a2a22316859d01.jpg

下面是这个方法中最重要的部分,也就是最终的注入。这个如下面第二个图所示,它最终也会调用registry.registerBeanDefinition()方法,也就是DefaultListableBeanFactory类的registerBeanDefinition()方法,最终将annotationClass注入到beanDefinitionMap和BeanDefinitionNames中。这个方法上面有说过,这里就不在说明了,这个方法中具体的细节会在之后的博文中详细分析。

112b6409f14fb97286da3add8c01a9f1ec0.jpg

bb077d68c5e68f8b32655f4593f6c0f93d6.jpg

至此我们的register()方法就处理脉络就说完了,我们在来看下此时的bean工厂的变化:

a53372167771f5ac12dfa59ff5aefa40171.jpg

505689b583b3aeecb391e7f418f54ee7cc1.jpg

其实就是如我上面说的那样将annotationClass(App.class)注入到了我们的bean工厂中。

2.4 refresh()

上面主要说了spring ioc容器启动过程的前两个方法,下面我们来看下spring ioc容器启动的最核心最重要的方法refresh()。

下面就让我们看下refresh()方法都做了什么?

dacba91f199a0363a37d3198965751b10c8.jpg

8156b9fc40047d1e1fb4f72a4199706ce9a.jpg

35f05885a75070b745b272c84cb852fc9db.jpg

上面是refresh()代码的主体部分,spring ioc容器初始化都会在这里完成,其内部代码非常复杂,这里我们就先了解下其处理脉络,看下每个方法都做了什么,在之后的博文中会详细讲解其处理细节。

2.4.1 prepareRefresh()

prepareRefresh():这个方法比较简单,主要是设置了启动时间、启动标识等操作,不是重点,先不用关注。

2.4.2 obtainFreshBeanFactory()

这个方法顾名思义,就是重新获取beanFactory实例,这个方法没有什么可说的,就是获取了之前我们初始化的beanFactory实例DefaultListableBeanFactory。

GenericApplicationContext:

3f58b0314f4f6149ee585e7f445d625d55a.jpg

2.4.3 prepareBeanFactory()

准备bean工厂初始化环境。主要是注入spring内部bean、注册Aware相关接口。

2.4.4 postProcessBeanFactory()

空方法。

2.4.5 invokeBeanFactoryPostProcessors()

这是比较重要的方法之一。其内部处理比较复杂,当然其处理方式是非常值得学习的,想要知道这个方法做了什么吗?下节我将带大家一起分析本方法和后续的处理方法的脉络部分。时间不早了,先洗洗睡了。

 

 

转载于:https://my.oschina.net/u/3759047/blog/3072555

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值