当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 ioc源码之前,有必要先了解下其核心类的含义及作用。上面是一个粗略的spring bean工厂内部存储,鉴于spring工厂比较庞大,上图也只画出了一部分比较重要的核心类,但是即便这样这个图片也已经看不清了,但是我会带您一个个讲解说明。
初始化spring容器测试代码:
首先既然是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中的哪个类应用了,大家自己想吧。
BeanDefinition:顾名思义,这个类是描述bean的接口。有两个重要的实现类分别是RootBeanDefinition和AnnotatedGenericBeanDefinition。RootBeanDefinition是用来定 义Spring内部的bean,如ConfigurationClassPostProcessor;AnnotatedGenericBeanDefinition是用来定义我们自定义的bean,也就是我们注入spring容器,需要spring管理的bean。
说完BeanDefinition,我们来认识下bean的元数据定义类。ClassMetadata:定义类的元数据,其中包括getClassName、isInterface、isAnnotation方法;MethodMetadata:定义方法的元数据;AnnotationMetadata:定义注解类元数据信息;
Spring初始化时bean是怎么存储的?答案是存储在DefaultListableBeanFactory类的集合中,其中BeanDefinitionMap是存储了beanName和BeanDefinition的映射,beanDefinitionNames存储了beanName,见下图:
但请注意此时bean还没有实例化,实例化后,bean是存储在DefaultSingletonBeanRegistry类中的singletonObjects中,这个我们在之后分析源码的时候会详细说。
AnnotatedBeanDefinitionReader:这个类是spring容器初始化的时候,在DefaultListableBeanFactory初始化之后就会实例化的一个类。它主要的作用是向spring bean工厂中注册我们自定义的bean,但是这个类其实是委托了BeanDefinitionReaderUtils类去调用DefaultListableBeanFactory类去完成bean的注册的,可见DefaultListableBeanFactory的重要性。
BeanDefinitionHolder:封装了beanName和BeanDefinition,不用关注。
BeanFactoryPostProcessor:bean工厂的后置处理器,可以插手bean工厂初始化过程,实现这个接口会得到beanFactory实例,也就是可以操作bean工厂了。至于什么时候会执行其实现类,那是后面博文会讲解的内容。
BeanPostProcessor:bean的后置处理器。会插手bean实例化的过程,实现此接口可以操纵容器中正在初始化的bean,也就是说可以对bean属性做修改。
BeanDefinitionRegistryPostProcessor:是个接口,实现了BeanFactoryPostProcessor接口,定义了postProcessorBeanDefinitionRegistry(registry)方法,也就是扩展了BeanFactoryPostProcessor接口,spring内部ConfigurationClassPostProcessor类实现了这个接口,去完成bean的解析。
实例化AnnotatedBeanDefinitionReader时,同时spring会向容器中注入7个spring自带的bean处理器。这些处理器在spring容器中都起着重要作用。这个也留待我们之后的博文中进行深入讲解。
ClassPathBeanDefinitionScanner:主要用于包的扫描工作。后续博文中会详细说。
以上简要带大家认识了spring ioc注入过程中涉及到的一些比较重要的spring核心类。
下面我们就以注解方式初始化spring为例讲解下spring容器初始化过程。
Spring注解方式启动:实例化AnnotationConfigApplicationContext,传入需要扫描的配置类或需要注入的beanclass。接着我们就来进入主题,看下源码入口:
如上图,首先会执行其父类的构造方法,正如我这个方法上注释的说明,主要实例化三个类,我们分别看下:
GenericApplicationContext:主要实例化了beanFactory,默认为DefaultListableBeanFactory。
AbstractApplicationContext:主要实例化PathMatchingResourcePatternResolver。这个类我在第一部分没有拿出来说明,其实可以理解它是资源文件解析器,提供了解析资源文件的功能。
执行完父类构造方法后,我们看下spring容器中的变化,即向spring容器中实例化了:
父类构造方法执行结束,我们就来看下主体方法中的第一个方法this()。它会调用无参的构造方法:
通过上面对代码的注解,你应该大致了解了这个构造方法做了什么。下面就在来看下里面的实现。
这个类最终主要会调用AnnotationConfigUtils.registerAnnotationConfigProcessors()方法,也就是AnnotatedBeanDefinitionReader会委托AnnotationConfigUtils类做一些事情,那么我们就看下registerAnnotationConfigProcessors()方法做了哪些事情。
正如这段代码中我注释所写的那样,主要是向bean工厂中注册spring内部自定义的bean。其中这些bean有的是实现了BeanPostProcessor、BeanFactoryPostProcessor接口的,这两个接口我们在第一部分中做过说明。被注册的6个bean在后续的操作中都有着各自的作用,这些类会在后续的博文中做详细的介绍。
这里每个bean都会封装成RootBeanDefinition(这个我们在第一部分也做过说明,RootBeanDefinition是封装spring内部自定义的bean的),最终会调用registry.registerBeanDefinition(beanName, definition)方法向容器中注册bean。Registry其实就是DefaultListableBeanFactory的实例,那么也就是调用DefaultListableBeanFactory类的registerBeanDefinition()方法:
上面是当前会执行的代码部分,也就是会将那6个bean都注册到beanDefinitionMap中。这也就是实例化AnnotatedBeanDefinitionReader类主要做的事情。
我们在来看下现在spring容器中的变化,其实就是bean工厂中注册了7个spring自定义的bean:
上面说了构造函数中的this.reader = new AnnotatedBeanDefinitionReader(this)方法,接下来说下构造函数中ClassPathBeanDefinitionScanner实例化的过程。
其实这个实例化的过程比较简单,除了实例化ClassPathBeanDefinitionScanner类之外,主要就是注册了几个注解:
暂时可以先不用管这部分,不重要。
此时就完成了spring容器初始化的第一步,再让我们来看下spring容器目前的情况:
接下来就要处理我们需要spring容器来管理的beanclass了。其实就是调用AnnotatedBeanDefinitionReader的register方法,其中参数AnnotatedClasses是我们传入的需要注入的bean,可以是一个带有@ComponentScan注解的配置类,也可以是一个bean集合,也可以是一个单个bean,所以此处做了循环操作处理。
最终会调用doRegisterBean()方法:
这个方法首先会调用shouldSkip()方法对beanclass做校验,具体校验逻辑见我对代码的注解说明:
接着会调用AnnotationConfigUtils.processCommonDefinitionAnnotations()方法,在这个方法中会给这个bean实例赋予属性,主要是通过这个beanclass上的注解来赋值,会看是否的@Lazy的、@Primary的值、@DependsOn的值、@Role的值、@Description的值,beanclass上存在这些注解就会取值并赋给bean实例。具体实现我们在之后的博文中会详细分析。
下面是这个方法中最重要的部分,也就是最终的注入。这个如下面第二个图所示,它最终也会调用registry.registerBeanDefinition()方法,也就是DefaultListableBeanFactory类的registerBeanDefinition()方法,最终将annotationClass注入到beanDefinitionMap和BeanDefinitionNames中。这个方法上面有说过,这里就不在说明了,这个方法中具体的细节会在之后的博文中详细分析。
至此我们的register()方法就处理脉络就说完了,我们在来看下此时的bean工厂的变化:
其实就是如我上面说的那样将annotationClass(App.class)注入到了我们的bean工厂中。
上面主要说了spring ioc容器启动过程的前两个方法,下面我们来看下spring ioc容器启动的最核心最重要的方法refresh()。
下面就让我们看下refresh()方法都做了什么?
上面是refresh()代码的主体部分,spring ioc容器初始化都会在这里完成,其内部代码非常复杂,这里我们就先了解下其处理脉络,看下每个方法都做了什么,在之后的博文中会详细讲解其处理细节。
prepareRefresh():这个方法比较简单,主要是设置了启动时间、启动标识等操作,不是重点,先不用关注。
这个方法顾名思义,就是重新获取beanFactory实例,这个方法没有什么可说的,就是获取了之前我们初始化的beanFactory实例DefaultListableBeanFactory。
GenericApplicationContext:
准备bean工厂初始化环境。主要是注入spring内部bean、注册Aware相关接口。
空方法。
这是比较重要的方法之一。其内部处理比较复杂,当然其处理方式是非常值得学习的,想要知道这个方法做了什么吗?下节我将带大家一起分析本方法和后续的处理方法的脉络部分。
Spring的设计思想及源码不是一朝一夕能弄透的,需要长期的积累和思辨。我已开启Spring系列源码分析的教程,有兴趣的小伙伴请关注我。如果本文对您有价值,不要忘了点赞、留言。