点击上方蓝色“方志朋”,选择“设为星标”

回复“666”获取独家整理的学习资料!

本文来源:http://r6f.cn/b47K

一. 前言

Spring家族特别庞大,对于开发人员而言,要想全面征服Spring家族,得花费不少的力气。俗话说,打蛇打七寸,那么Spring家族的“七寸”是什么呢?我心目中的答案一直都是 Spring Framework!

本篇文章记录我自己在学习Spring Framework的过程中的一小部分源码解读和梳理,来谈一谈Spring 容器在启动过程中是如何扫描Bean的。

二. 学习方法论

我相信每个想变成优秀的开发人员都想弄懂Spring源码,我亦如此。于是通过很多途径来找Spring源码的学习资料、买书、看视频等等。到头来发现只有自己静下心来一步一步跟着源码调试,一行一行的深入理解,才能深入理解Spring的奥妙!这个过程很枯燥,但优秀的猎手最能耐得住寂寞和浮躁!

我们知道,Spring容器的启动方式有多种:XML文件、注解、Java Config。在实际的使用中并不是选择其中某一种,而是相互搭配。其底层的容器启动过程是一样的,只是入口变了而已。另外,学习Spring的最佳方式就是自己将源码工程构建出来,这样便于源码阅读、备注、修改。构建出来的工程长这样:

三. 代码入口

话不多说直接开干!代码入口如下:

@Configuration
@ComponentScan("com.leon.funddatahouse")
public class Config {
}public class MyApplication {public static void main(String[] args) {// 我们基于注解的方式AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Config.class);// 如果基于XML文件配置,则也可以如下:// ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml");}
}

在构造方法中,总共做了3件事情。这三件事情包括了整个Spring容器启动的所有过程!啃碎他们,便成功了一半!

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {// 1.调用默认构造方法,进行容器环境的准备this();// 2.基于配置类注册相关信息register(componentClasses);// 3.刷新整个容器refresh();}

四. 解析之前

在解析之前,先将容器和BeanFactory的UML类图放出。原因在于它们担任的角色、具备的功能太多太强大了,同时这也增加了源码理解的难度。因此这里先放出UML类图作为手册查看,便于理解源码。

4.1 容器UML类图

4.2 BeanFactoryUML类图

五. 源码解析

5.1 构造方法解析

5.1.1 初始化容器中的BeanFactory

在构造方法中,显式的调用了this(),既无参构造方法:

public AnnotationConfigApplicationContext() {// 1.实例化容器中的reader. 后面会详细解析this.reader = new AnnotatedBeanDefinitionReader(this);// 2.实例化容器中的scanner.后面会详细解析this.scanner = new ClassPathBeanDefinitionScanner(this);
}

乍看一眼,这个无参构造方法做了两件事情,其实不然。它实际上等同于:

public AnnotationConfigApplicationContext() {// 1.调用父类构造方法super();// 2.实例化容器中的reader. 后面会详细解析this.reader = new AnnotatedBeanDefinitionReader(this);// 3.实例化容器中的scanner.后面会详细解析this.scanner = new ClassPathBeanDefinitionScanner(this);
}

这一点很关键, 如果没有意识到这里隐形调用了父类构造方法的话, 那么接下来的路没法走, 因为在父类构造器中做了一件大事情:

// 在父类的构造方法中, 创建了容器中的BeanFactory.至此,容器中有了第一个程序创建的属性:beanFactory
public GenericApplicationContext() {// 初始化容器的beanFactory,类型为DefaultListableBeanFactorythis.beanFactory = new DefaultListableBeanFactory();
}

BeanFactory 和 FacotryBean的区别, 请点击这里

5.1.2 实例化容器中的Reader

reader最主要的目的是用于辅助注册BeanDefinition,其具体的使用后文在介绍,这里我们只需知道它包含了哪些东西。

// 入参registry就是容器本身。因为通过上面的UML类图可以发现,容器间接继承了BeanDefinitionRegistry
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {// getOrCreateEnvironment() 方法最主要是获取环境。实际类型其实默认的就是StandardEnvironment类。这里的环境包括两方面:// 1.systemEnvironment:操作系统环境。这样,Spring就可以获取到操作系统、CPU核心数等操作系统本身的数据。// 2.systemProperties:JVM的环境变量。这样,Spring就可以获取到JVM的基础数据,比如我们在启动参数中手动设置的环境变量等。this(registry, getOrCreateEnvironment(registry));
}

这里通过this() 调用了reader内部另一个构造方法:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");Assert.notNull(environment, "Environment must not be null");// 设置registry,已经知道它的就是容器本身:AnnotationConfigApplicationContextthis.registry = registry;// 创建条件处理器this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);// 非常关键!提前往容器中注册一些必要的后置处理器AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

这个构造方法很重要, 因为它涉及到spring容器当中的两个重要成员:条件解析器和后置处理器!

5.1.2.1 实例化条件处理器

相信熟悉Spring的人一定都知道或用过@ConditionalOnBean / @ConditionalOnClass 等条件注解.而这些条件注解的解析就是ConditionEvaluator.

public ConditionEvaluator(@Nullable BeanDefinitionRegistry registry,@Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {// 实际上是委托给内部类ConditionContextImplthis.context = new ConditionContextImpl(registry, environment, resourceLoader);
}// ------------分割线------------------// 内部的ConditionContextImpl构造器
public ConditionContextImpl(@Nullable BeanDefinitionRegistry registry,@Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {// 再说一遍,registry的实际类型就是 AnnotationConfigApplicationContthis.registry = registry;// 获取beanFactory,我们也知道了beanFactory其实就是 ConfigurableListableBeanFactorythis.beanFactory = deduceBeanFactory(registry);// 从容器中获取environment,前面介绍过,容器中的environment的封装类是 StandardEnvironmentthis.environment = (environment != null ? environment : deduceEnvironment(registry));// 资源加载器. 通过UML类图可以发现,resourceLoader就是容器, 因为容器间接继承了ResourceLoaderthis.resourceLoader = (resourceLoader != null ? resourceLoader : deduceResourceLoader(registry));// 类加载器. 实际上就是获取beanFactory的类加载器。理应如此,容器当中的类加载器肯定要一致才行this.classLoader = deduceClassLoader(resourceLoader, this.beanFactory);
}

后面在解析BeanDefinition时我们还会遇到ConditionEvaluator, 其具体源码解析会用专门的文章来解析,本篇文章我们只需要知道它的作用即可.

5.1.2.2 注册一部分后置处理器

ConditionEvaluator初始化完成之后,接下来就特别重要了,因为在这里将提前注入一些后置处理器:

public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {// 空壳方法,实际委托给重载的方法registerAnnotationConfigProcessors(registry, null);
}

重载的方法如下(高能预警):

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {// 获取容器中的beanFactory,通过前面的解析,我们知道,这里一定会获取到。因此将进入if分支DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);if (beanFactory != null) {// 此时beanFactory的属性dependencyComparator为null,因为初始化过程中,内部成员变量如果没有默认值,则默认为null,// 所以如果第一次进来, 这里的判断一定成立,对dependencyComparator进行设置。// AnnotationAwareOrderComparator继承了OrderComparator,// 因此可以对实现了Ordered接口、打上@Order或者@Priority注解的类进行排序。// 也就是说,在这里设置beanFactory中的orderComparator,以支持解析bean的排序功能。if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);}// beanFactory初始化时,默认为SimpleAutowireCandidateResolver,因此第一次进来时这里的判断也一定成立。// ContextAnnotationAutowireCandidateResolver最主要的作用就是支持@Lazy注解的类的处理。if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());}}// 初始化一个bdh容器,用于盛放接下来将解析出来的后置处理器的bd。Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);// 容器在第一次初始化时,内部一个bd都没有的。// 也就是说,从这里开始,容器将第一次装载bd,而这里的这些bd都是spring自带的后置处理器。// 获取并注册ConfigurationClassPostProcessor后置处理器 的bdif (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));}// 获取并注册AutowiredAnnotationBeanPostProcessor后置处理器 的bdif (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));}// 获取并注册CommonAnnotationBeanPostProcessor后置处理器 的bdif (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));}// 获取并注册PersistenceAnnotationBeanPostProcessor后置处理器 的bdif (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition();try {def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,AnnotationConfigUtils.class.getClassLoader()));}catch (ClassNotFoundException ex) {throw new IllegalStateException("Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);}def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));}// 获取并注册EventListenerMethodProcessor后置处理器 的bdif (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));}// 获取并注册DefaultEventListenerFactory后置处理器 的bdif (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));}return beanDefs;
}

这个方法首次出现了BeanDefinition这个类. Spring的BeanDefinition相当于Java的Class

通过该方法之后, beanFactory中就存在了以上6个bd:

曾经有人跟我说, 掌握了Spring的后置处理器, 那么整个Spring就掌握了10%! 可见其重要性. 但是在这里先不展开后置处理器(太多了),本篇文章的主线是容器启动过程。

5.1.2.3 reader初始化过程小结

到这里reader部分的初始化终于完成了。总结一下,reader的初始化主要干了这些事情:1.创建并设置容器当中的Environment属性。即默认为StandardEnvironment类。2.创建并设置容器当中的条件解析器,即ConditionEvaluator,其内部实际委托给内部类ConditionContextImpl。3.注册6个后置处理器到容器当中。注意这里仅是生成了后置处理器的BeanDefinition。还并没有进行bean解析和后置处理的执行。

5.1.3 实例化容器中的Scanner

解析完reader之后,继续解析scanner。这里的scanner的实际类型是ClassPathBeanDefinitionScanner。它最主要的目的就是扫描类路径下所有的class文件能否解析为bd。其最终调用的构造方法如下:

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {// 1.委托给内部的另一个构造方法this(registry, true);
}
// ------------------------分割线-------------------------
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {// 2.又委托给内部的另一个构造方法 >_<// 从上面的入参可以知道 入参的registry实际就是容器本身, 并使用默认的filter.这个filter干什么的,下面会解析this(registry, useDefaultFilters, getOrCreateEnvironment(registry));
}
// ------------------------分割线-------------------------
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,Environment environment) {// 3.又委托给内部的另一个构造方法 T^Tthis(registry, useDefaultFilters, environment,(registry instanceof ResourceLoader ? (ResourceLoader) registry : null));
}
// ------------------------分割线-------------------------
// 4. 终于见到了庐山真面目(不容易) ^_^
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,Environment environment, @Nullable ResourceLoader resourceLoader) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");// 再说一遍, registry就是容器!this.registry = registry;// 重要!!! 是否包括默认过滤器。从上面的入参可以知道, 这里的useDefaultFilters = true,因此会进入if分支if (useDefaultFilters) {registerDefaultFilters();}// 设置环境变量setEnvironment(environment);// 设置资源加载器setResourceLoader(resourceLoader);
}

5.1.3.1 registerDefaultFilters()方法

从最终的构造方法我们知道, Scanner在扫描的过程中,会使用过滤策略,并且使用了默认的过滤策略.默认策略就是以下这个方法解析.

protected void registerDefaultFilters() {// 扫描@Component注解的类this.includeFilters.add(new AnnotationTypeFilter(Component.class));ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();try {// 扫描所有@ManageBean的类this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");}catch (ClassNotFoundException ex) {}try {// 扫描所有@Named的类this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");}catch (ClassNotFoundException ex) {}
}

这里的一个知识点:@ManageBean和@Named的作用和@Component是一样的。只是我们通常习惯使用@Component。

为什么这里没有添加默认扫描@Service、@Repository、@Controller呢?原因很简单,这些注解都间接继承了@Component了。到这里,scanner解析完毕,它做的最主要的事情就是添加默认的过滤器策略以便在后续中可以扫描出@Component注解的类。

六 默认构造方法小结

现在我们再来看一下构造方法:

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {// 1.调用默认构造方法,进行容器环境的准备this();register(componentClasses);refresh();
}

从入口看, 就只有这三行代码, 但其中的第一行,调用默认构造方法就做了这么多准备工作,其中也牵扯出了一些Spring整个体系中最重要的几个组件,比如BeanFactory / BeanDefinition / BeanDefinitionReader / BeanDefinitionScanner / Environment / ConditionEveluator / PostProcessor等等.随便拿一个出来都够喝一壶! 这些点会各个击破, 但不是本篇文章的重点,本篇文章的重点是先梳理整个启动过程的第一步: 构造方法的执行过程.

热门内容:
  • 今天终于搞懂了:为什么 Java 的 main 方法必须是 public static void?

  • 七个开源的 SpringBoot 前后端分离项目,Star过千,快去收藏夹吃灰吧!

  • 道友自诉:入职中软一个月(外包华为)就离职了!

  • 腾讯推出高性能 RPC 开发框架

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
明天见(。・ω・。)

Spring 容器的启动过程相关推荐

  1. spring容器_Spring 容器的启动过程探秘

    一. 前言 Spring家族特别庞大,对于开发人员而言,要想全面征服Spring家族,得花费不少的力气.俗话说,打蛇打七寸,那么Spring家族的"七寸"是什么呢?我心目中的答案一 ...

  2. Spring容器的启动流程

    (本文基于 Spring 的 5.1.6.RELEASE 版本) Spring的启动流程可以归纳为三个步骤: 1.初始化Spring容器,注册内置的BeanPostProcessor的BeanDefi ...

  3. Spring容器的初始化过程

    1.Spring 容器高层视图 Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表,然后根据这张注册表实例化Bean,装配号Bean之间的依 ...

  4. idea中生成spring的 xml配置文件_【132期】面试再被问到Spring容器IOC初始化过程,就拿这篇文章砸他~...

    点击上方"Java面试题精选",关注公众号 面试刷图,查缺补漏 >>号外:往期面试题,10篇为一个单位归置到本公众号菜单栏->面试题,有需要的欢迎翻阅 阶段汇总集 ...

  5. 【132期】面试再被问到Spring容器IOC初始化过程,就拿这篇文章砸他~

    程序员的成长之路 互联网/程序员/技术/资料共享 关注 阅读本文大概需要 14 分钟. 作者:拥抱心中的梦想 juejin.im/post/5ab30714f265da237b21fbcc 一.老规矩 ...

  6. Spring容器IOC初始化过程—今天终于进行总结了

    https://www.colabug.com/2539499.html 作为一个经常使用Spring的后端程序员,小编很早就想彻底弄懂整个Spring框架了!但它整体是非常大的,所有继承图非常复杂, ...

  7. Docker容器的启动过程(七)

    当我们输入下面这行命令,启动一个ubuntu容器时,到底发生了什么? docker run -i -t ubuntu /bin/bash 大致过程可以用下图描述: 首先系统要有一个docker dae ...

  8. 重新学习Spring一--Spring在web项目中的启动过程

    1 Spring 在web项目中的启动过程 Spring简介 Spring 最简单的功能就是创建对象和管理这些对象间的依赖关系,实现高内聚.低耦合.(高内聚:相关性很强的代码组成,既单一责任原则:低耦 ...

  9. 【spring容器启动】之bean的实例化和初始化(文末附:spring循环依赖原理)

    本次我们通过源码介绍ApplicationContext容器初始化流程,主要介绍容器内bean的实例化和初始化过程.ApplicationContext是Spring推出的先进Ioc容器,它继承了旧版 ...

最新文章

  1. 在微信小程序的JS脚本中使用Promise来优化函数处理
  2. python【Pandas科学计算库】连女朋友都会用的Pandas(真の能看懂~!)
  3. 2021暑假每日一题 【week6 完结】
  4. OpenCASCADE:建模算法之隐藏线去除
  5. RxSwift之深入解析场景特征序列的使用和底层实现
  6. Highcharts:小案例,自定义图片下载路径,中文乱码的解决办法(不足之处,求指点)。...
  7. JavaScript、PHP、Golang、Haskell、Elixir,哪个才是最佳编程语言?
  8. 安卓交叉编译环境配置
  9. 小米抢购软件_【晓满晓满晓】今日科技新鲜事:小米11、鸿蒙os、摩拜单车
  10. 高频电子线路实验箱QY-JXSY25
  11. 如何选择jquery版本
  12. 毕业论文word版 格式设置技巧
  13. 【WPS表格】一些简单函数
  14. 计算机exsl表f4代表锁定,(Excel中F4是什么键)笔记本锁定单元格f4怎么按
  15. 吴恩达-机器学习-简单决策树
  16. vue使用阿里字体教程(引入外部字体)
  17. 微信企业支付 服务器根证书,微信支付服务器证书根ca证书有什么用
  18. 华为开发者学堂上线《HarmonyOS Connect开发》系列课
  19. Harbor: Harbor卸载安装及基本使用教程
  20. Jmeter Kafka插件开发之Sampler篇

热门文章

  1. 《DSP using MATLAB》Problem 6.3
  2. 使用laravel框架的eloquent\DB模型连接多个数据库
  3. 未能加载文件或程序集“Report.Basic”或它的某一个依赖项。试图加载格式不正确的程序...
  4. [LeetCode] Add Digits
  5. Matlab随笔之矩阵入门知识
  6. ORACLE分页SQL
  7. 【MATLAB】单元数组类型
  8. AnimeGANv2 实现动漫风格迁移,简单操作
  9. 使用深度学习阅读和分类扫描文档
  10. Facebook 正在研究新型 AI 系统,以自我视角与世界进行交互