前言
invokeBeanFactoryPostProcessors 会执行 BeanFactory 的后置处理器。看到这里会有疑问:
什么是 BeanFactoryPostProcessor ?
BeanfactoryPostProcessor 该如何使用?
知道了上面两个问题的答案,对 BeanFactoryPostProcessor 有了了解之后,然后再深入源码,继续阅读 invokeBeanFactoryPostProcessors 这个方法。
作用
资料还是在官网可以找到答案:
阅读了一下,大概意思是 Spring IoC 容器允许 BeanFactoryPostProcessor 读取配置元数据,并有可能在容器实例化除 BeanFactoryPostProcessor 实例以外的任何 bean 之前更改它。
同样可以使用 Ordered 接口对 BeanFactoryPostProcessor 进行排序。
注意
BeanFactoryPostProcessor 操作的是 BeanDefinition ,即元数据。但是同样可以通过获取到 BeanFactory 进行实 ...
前言
根据 refresh 流程,当 obtainFreshBeanFactory 执行结束后,下一步会执行 prepareBeanFactory ,顾名思义,这个方法主要是准备 BeanFactory,下面一起看一看这部分逻辑。
prepareBeanFactory
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // Tell the internal bean factory to use the context's class loader etc. // 设置beanFactory的类加载器 beanFactory.setBeanClassLoader(getClassLoader()); // spring.spel.ignor ...
前言
前面的准备工作结束之后,就是进入核心代码 refresh。
源码
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273public void refresh() throws BeansException, IllegalStateException { // 加锁 synchronized (this.startupShutdownMonitor) { StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // 准备一些上下文 prepareRefresh(); // 获取准备后的 beanFactory DefaultListable ...
前言
看完无参构造的源码及流程之后,后面将会执行 register 方法。
register 方法,入参是我们的配置类 JavaConfig.class ,下面跟着源码继续往下走!
源码分析
this.reader.register(componentClasses);
这里执行的是 reader 的方法,入参就是传入的 JavaConfig.class。
reader 在之前初始化,就是注册一些 PostProcessor。
循环,注册所有的配置类;
doRegisterBean 开始注册。
doRegisterBean 才是真正注册 Bean 的逻辑,从名字也可以有所猜测。下面开始进入 doRegisterBean 的源码阅读:
doRegisterBean
此处代码较长,以截图代替。
通过源码可以看出,这块主要流程:
校验传入的 JavaConfig.class 的注解(是否需要忽略);
处理通用注解;
封装为 BeanDefinitionHolder 后,注册到容器中。
debug
通过 debug 可以看出,在执行完 register 之后,相当于将 J ...
前言
AnnotationConfigApplicationContext 构造函数除了初始化一个 reader ,还有一个 scanner,下面来一起看看 ClassPathBeanDefinitionScanner 都有什么逻辑。
源码分析
this.scanner = new ClassPathBeanDefinitionScanner(this); 代码如下所示:
其中 useDefaultFilters 默认设置的 true。所以最终会执行下面三部分代码:
registerDefaultFilters();
setEnvironment(environment);
setResourceLoader(resourceLoader);
再来看下 UML :
ClassPathBeanDefinitionScanner 继承了 ClassPathScanningCandidateComponentProvider,而上面说的这三个方法,其实都是父类 ClassPathScanningCandidateComponentProvider 的方法。
对应的这三个操作就是给它 ...
前言
BeanDefinition 的概念也了解了,也知道一个 Bean 在 Spring 中定义的信息有哪些之后,继续言归正传。
源码分析
在初始化时会先生成一个 reader ,进入方法,其实是走的下面的逻辑:
其中 getOrCreateEnvironment(registry) 会返回一个 Environment 用来表示当前的运行环境之类的。
ConditionEvaluator 是用来完成对 @Conditional 这个条件注解的判断。
这块可以参考官网:Environment Abstraction 章节
Conditionally Include @Configuration Classes or @Bean Methods 章节
补充
BeanDefinitionRegistry:就是对 BeanDefinition 进行注册、移除、获取等操作的一个接口。
比如:registerBeanDefinition、removeBeanDefinition、containsBeanDefinition 看名字也能猜个大概意思。
registerAnnotationC ...
前言
BeanDefinition:顾名思义,就是 Bean 的定义,是用来描述一个 Bean 都有什么信息。前面说在初始化 DefaultListableBeanFactory 时,会初始化一个 Map<String, BeanDefinition>,这个 Map 的功能暂且不说,(PS:查资料说的是存储 bean),所以今天就结合官方文档以及源码,一起了解一下 BeanDefinition!
概念
在容器内部,使用 BeanDefinition 对象定义一个 Bean。而定义的信息包含 类名、作用域、是否懒加载、构造参数、初始化方法、销毁方式等等。
了解了概念之后,开始阅读源码,源码部分比较长。
源码介绍
通过 UML 可以看出 BeanDefinition 接口继承了 AttributeAccessor 和 BeanMetadataElement 两个接口。
AttributeAccessor
AttributeAccessor:定义用于将元数据附加到任意对象或从任意对象访问元数据的通用协定的接口。
1234567891011121314public interf ...
前言
在前一篇文章:创建 IoC 容器的几种方式中,介绍了四种方式,这里以 AnnotationConfigApplicationContext 为例,跟进代码,看看 IoC 的启动流程。
入口
从 JavaConfig 中加载配置的 AnnotationConfigApplicationContext 启动方式如下:
进去之后发现构造其实是调用的当前无参构造。
所以在启动时也可以直接声明无参构造,改写为下面这种:
12345678910111213public class AnnotationConfigApplicationTest { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(JavaConfig.class); context.refresh(); System.out.println(context ...
前言
在上一篇文章末尾画了一幅简图,现在从简图这入手,先来看看如何创建容器!
创建容器
在搭建 Spring 源码阅读环境 时,最后举了一个例子,其实就是创建容器,并从容器中获取 Bean ,来测试环境是否 OK。
根据元数据的不同,创建容器的方式也不同,下面参考官方文档,简单介绍下创建容器的几种方式:
Java 配置获取元数据
12345678910111213public class AnnotationConfigApplicationTest { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(JavaConfig.class); context.refresh(); System.out.println(context.getBean(UserComponent.class)); }} ...
前言
在前一篇文章中介绍了如何构建源码阅读环境,既然构建好了源码环境,本地也可以正常运行,那就开始阅读源码吧!
在阅读源码时,会参考官方文档,很多概念在官网都可以得到答案,有兴趣的小伙伴们可以继续阅读,当做复习,写的不足之处,希望多多指导。
IoC 和 DI
IoC
IoC(Inversion of Control),即控制反转。
之前是在对象内部 new 创建其他对象,然后使用。
而现在 Spring 中有一个容器可以在创建管理这些对象,并且将对象依赖的其他对象注入到这个对象中,这些对象的创建、销毁都由 Spring 进行管理。
相比以前来说,不再由自己控制其他对象的生命周期,这个过程就叫做控制反转。而负责统一管理这些类的容器就叫做 IoC 容器。
DI
IoC is also known as dependency injection (DI).
是不是感觉奇奇怪怪的,为什么说:IoC 也称为 DI。
其实 IoC 和 DI 是同一个概念的不同角度描述。
依赖注入是指组件之间的依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。
通过依赖注入机制,我 ...
前言
本文记录了 Spring 源码环境的搭建方式,以及踩过的那些坑! 当前版本:5.3.2-SNAPSHOT。
环境准备
Git
JDK
master 分支需要 JDK 11
5.2.x 分支, JDK8 即可
Gradle 6.5.1
IDEA 最新 (2020.2.3)
Spring 源码仓库地址:https://github.com/spring-projects/spring-framework
下载源码
clone 源码
1git clone https://github.com/spring-projects/spring-framework.git
使用 IDEA 打开
等待 IDEA 加载完成即可。
注: 也可以指定 clone 的分支
1git clone -b 5.2.x https://github.com/spring-projects/spring-framework.git
或者先 fork 到自己的仓库,然后再 clone。
这里我是 fork 到我的仓库,然后再 clone 的。
当前 master 分支代表的 ...
前言
有小伙伴反馈说希望可以自定义 Markdown 模版,这样就可以导出自己想要的样式了!这个功能可以有,毕竟大家不可能都生成一模一样的文档。现在来一起看看如何实现自定义模版吧!
设置模版
Settings -> Other Settings -> Doc View -> Markdown Template
打开之后内容如下:
模版这里分两个 Tab 分别是 Spring 、 Dubbo 。
这两个模版内容就稍微有些区别,如果自定义模版,直接修改保存即可。
模版变量
关于模版变量,这里使用的是 velocity 将变量替换为文本,如果有其他方式,或者说是 IDEA 用的那种方式,希望可以告诉我。最好可以提供源码。
总结
非常感谢 lvgo 小伙伴的参与。
如果小伙伴们在使用过程中发现一些 bug 或者有好的意见建议,可以在 GitHub 提交 Issues 留言指出来。
Doc View GitHub:https://github.com/liuzhihang/doc-view
最后,记得要 一键三连, 来个 【分享、点赞、在看】!
推荐文章
Dubbo ...