SpringBoot启动流程分析(五):SpringBoot自动装配原理实现

SpringBoot系列文章简介

SpringBoot源码阅读辅助篇:

  Spring IoC容器与应用上下文的设计与实现

SpringBoot启动流程源码分析:

  1. SpringBoot启动流程分析(一):SpringApplication类初始化过程
  2. SpringBoot启动流程分析(二):SpringApplication的run方法
  3. SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法
  4. SpringBoot启动流程分析(四):IoC容器的初始化过程
  5. SpringBoot启动流程分析(五):SpringBoot自动装配原理实现
  6. SpringBoot启动流程分析(六):IoC容器依赖注入

笔者注释版Spring Framework与SpringBoot源码git传送门:请不要吝啬小星星

  1. spring-framework-5.0.8.RELEASE
  2. SpringBoot-2.0.4.RELEASE

自定义Starter:

  SpringBoot应用篇(一):自定义starter

一、前言

  上一篇文章,通过分析refresh()方法中的invokeBeanFactoryPostProcessors()方法,分析了IoC容器的初始化过程,这一节从代码上如下所示,接上一节ConfigurationClassParser类中的parse()方法,接着分析SpringBoot的自动装配原理。

 1 // ConfigurationClassParser类
 2 public void parse(Set<BeanDefinitionHolder> configCandidates) {
 3     this.deferredImportSelectors = new LinkedList<>();
 4     for (BeanDefinitionHolder holder : configCandidates) {
 5         BeanDefinition bd = holder.getBeanDefinition();
 6         try {
 7             // 如果是SpringBoot项目进来的,bd其实就是前面主类封装成的 AnnotatedGenericBeanDefinition(AnnotatedBeanDefinition接口的实现类)
 8             if (bd instanceof AnnotatedBeanDefinition) {
 9                 parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
10             } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
11                 parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
12             } else {
13                 parse(bd.getBeanClassName(), holder.getBeanName());
14             }
15         } catch (BeanDefinitionStoreException ex) {
16             throw ex;
17         } catch (Throwable ex) {
18             throw new BeanDefinitionStoreException(
19                     "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
20         }
21     }
22     // 加载默认的配置---》(对springboot项目来说这里就是自动装配的入口了)
23     processDeferredImportSelectors();
24 }

二、SpringBoot自动装配原理。

2.1、@SpringBootApplication注解

  对这个注解详细大家一定非常熟悉了。再来好好看看这个注解。

 1 @Target(ElementType.TYPE)
 2 @Retention(RetentionPolicy.RUNTIME)
 3 @Documented
 4 @Inherited
 5 @SpringBootConfiguration
 6 @EnableAutoConfiguration
 7 @ComponentScan(excludeFilters = {
 8         @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
 9         @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
10 public @interface SpringBootApplication {
11     ...
12 }

  接着看@EnableAutoConfiguration

1 @Target(ElementType.TYPE)
2 @Retention(RetentionPolicy.RUNTIME)
3 @Documented
4 @Inherited
5 @AutoConfigurationPackage
6 @Import(AutoConfigurationImportSelector.class)
7 public @interface EnableAutoConfiguration {
8     ...
9 }

  OK,看到@Import(AutoConfigurationImportSelector.class)导入了一个重要的类AutoConfigurationImportSelector。

2.2、AutoConfigurationImportSelector

 1 // AutoConfigurationImportSelector类
 2 //自动装配
 3 @Override
 4 public String[] selectImports(AnnotationMetadata annotationMetadata) {
 5     if (!isEnabled(annotationMetadata)) {
 6         return NO_IMPORTS;
 7     }
 8     AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
 9             .loadMetadata(this.beanClassLoader);
10     AnnotationAttributes attributes = getAttributes(annotationMetadata);
11     //获取所有的自动配置类(META-INF/spring.factories中配置的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的类)
12     List<String> configurations = getCandidateConfigurations(annotationMetadata,
13             attributes);
14     configurations = removeDuplicates(configurations);
15     //需要排除的自动装配类(springboot的主类上 @SpringBootApplication(exclude = {com.demo.starter.config.DemoConfig.class})指定的排除的自动装配类)
16     Set<String> exclusions = getExclusions(annotationMetadata, attributes);
17     checkExcludedClasses(configurations, exclusions);
18     //将需要排除的类从 configurations remove掉
19     configurations.removeAll(exclusions);
20     configurations = filter(configurations, autoConfigurationMetadata);
21     fireAutoConfigurationImportEvents(configurations, exclusions);
22     return StringUtils.toStringArray(configurations);
23 }

  至于怎么从章节一中提到的ConfigurationClassParser类中的parse()===>processDeferredImportSelectors()==>AutoConfigurationImportSelector#selectImports(),篇幅有限不做过多介绍。

   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

  我们来看一下getCandidateConfigurations()方法是怎么拿到这些自动配置类的。

// AutoConfigurationImportSelector类1 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
2                                                   AnnotationAttributes attributes) {
3     List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
4             getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
5     Assert.notEmpty(configurations,
6             "No auto configuration classes found in META-INF/spring.factories. If you "
7                     + "are using a custom packaging, make sure that file is correct.");
8     return configurations;
9 }

  是不是又看到一个十分熟悉的方法loadFactoryNames(),没错,其实我们在分析SpringBoot启动流程的第一篇文章的时候,就已经分析了,SpringBoot是如何从META-INF/spring.factories中加载指定key的value的。ok,我们在这里再次回顾一遍。

  看看loadFactoryNames()方法

// SpringFactoriesLoader类1 public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
2     String factoryClassName = factoryClass.getName();
3     return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
4 }

  debug,看看要从META-INF/spring.factories中加载的类的key,如下图所示:org.springframework.boot.autoconfigure.EnableAutoConfiguration

  回到selectImports()方法,debug,跳过List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);看一下configurations

  竟然有110个,那这些类都在哪里呢?看spring-boot-autoconfigure(当然在SpringBoot的工程中,也不止这一个依赖包中存在该配置文件)工程下的META-INF/spring.factories,我们能看到org.springframework.boot.autoconfigure.EnableAutoConfiguration定义了一大堆。

  其中还有一个com.demo.starter.config.DemoConfig是我自定义的starter。如下所示,我在测试工程中添加了自定义starter的依赖,所以SpringBoot就能扫描到。

1 <dependency>
2     <groupId>com.demo</groupId>
3     <artifactId>demo-spring-boot-starter</artifactId>
4     <version>0.0.1-RELEASE</version>
5 </dependency>

  继续看Set<String> exclusions = getExclusions(annotationMetadata, attributes);方法,该方法是排除主类上@SpringBootApplication注解上排除的自动装配的类。比如我们在该注解上排除我们自定义starter的自动装配的类,@SpringBootApplication(exclude = {com.demo.starter.config.DemoConfig.class})(当然也可以用excludeName进行排除),那么在后面的configurations.removeAll(exclusions);方法中将会删除我们的com.demo.starter.config.DemoConfig.class。

  configurations = filter(configurations, autoConfigurationMetadata);该行代码将会过滤掉不需要装配的类。过滤的逻辑有很多,比如我们常用的@ConditionXXX注解。如下所示:

 1 @ConditionalOnBean:容器中有指定的Bean
 2 @ConditionalOnClass:当类路径下有指定的类
 3 @ConditionalOnExpression:基于SpEL表达式作为判断条件
 4 @ConditionalOnJava:基于JVM版本作为判断条件
 5 @ConditionalOnJndi:在JNDI存在的条件下查找指定的位置
 6 @ConditionalOnMissingBean:当容器中没有指定Bean的情况下
 7 @ConditionalOnMissingClass:当类路径下没有指定的类
 8 @ConditionalOnNotWebApplication:当前项目不是Web项目
 9 @ConditionalOnProperty:配置文件中指定的属性是否有指定的值
10 @ConditionalOnResource:类路径下是否有指定的资源
11 @ConditionalOnSingleCandidate:当指定Bean在容器中只有一个,或者虽然有多个但是指定首选Bean
12 @ConditionalOnWebApplication:当前项目是Web项目的条件下

  至于如何将这些类解析成BeanDefinition并注册进beanDefinition中的,和上一节讲的过程是一样的,不再赘述了。

  debug,跳过refresh()方法中的invokeBeanFactoryPostProcessors(beanFactory);方法。如下图所示,最终在beanFactory的BeanDefinitionMap中找到了自定义starter中的自动装配的类。

  综合本文和上一篇博文我们详细的梳理了IoC容器的初始化过程,到此IoC容器的初始化过程就结束了。

  

  原创不易,转载请注明出处。

  如有错误的地方还请留言指正。

原文地址:https://www.cnblogs.com/hello-shf/p/11057861.html

时间: 2024-10-07 01:52:01

SpringBoot启动流程分析(五):SpringBoot自动装配原理实现的相关文章

SpringBoot启动流程分析(四):IoC容器的初始化过程

SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一):SpringApplication类初始化过程 SpringBoot启动流程分析(二):SpringApplication的run方法 SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法 SpringBoot启动流程分析(四

Uboot启动流程分析(五)

1.前言 在前面的文章Uboot启动流程分析(五),链接如下: https://www.cnblogs.com/Cqlismy/p/12147411.html 已经对board_init_f() 函数作出了简单的分析,该函数对一些早期的外设进行了初始化,例如调试串口,并填充了gd_t结构体中的成员变量,最主要的是对整个DRAM的内存进行了分配,以便uboot的重定位,接下来,先回顾一下_main函数的大概流程,如下: _main | board_init_f_alloc_reserve-->re

SpringBoot:认认真真梳理一遍自动装配原理

Spring翻译为中文是“春天”,的确,在某段时间内,它给Java开发人员带来过春天,但是随着我们项目规模的扩大,Spring需要配置的地方就越来越多,夸张点说,“配置两小时,Coding五分钟”. 这种纷繁复杂的xml配置随着软件行业一步步地发展,必将逐步退出历史舞台. SpringBoot介绍 来自:百度百科 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样

SpringBoot 启动流程

SpringBoot 启动流程 加载 resources/META-INF/spring.factories 中配置的 ApplicationContextInitializer 和 ApplicationListener. /** * 加载在框架内部使用的各种通用工厂 bean. * spring.factories 文件必须满足 Properties 文件格式,属性的 key 是接口或抽象类的全限定类名, * value 是一组由逗号分隔的实现类全类名. */ public final cl

springboot启动流程(一)构造SpringApplication实例对象

所有文章 https://www.cnblogs.com/lay2017/p/11478237.html 启动入口 本文是springboot启动流程的第一篇,涉及的内容是SpringApplication这个对象的实例化过程.为什么从SpringApplication这个对象说起呢?我们先看一段很熟悉的代码片段 @SpringBootApplication public class SpringBootLearnApplication { public static void main(Str

SpringBoot启动机制(starter机制)核心原理详解

作者:MyBug 一.前言 使用过springboot的同学应该已经知道,springboot通过默认配置了很多框架的使用方式帮我们大大简化了项目初始搭建以及开发过程.本文的目的就是一步步分析springboot的启动过程,这次主要是分析springboot特性自动装配.那么首先带领大家回顾一下以往我们的web项目是如何搭建的,通常我们要搭建一个基于Spring的Web应用,我们需要做以下一些工作:pom文件中引入相关jar包,包括spring.springmvc.redis.mybaits.l

u-boot启动流程分析(2)_板级(board)部分

转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global data介绍以及背后的思考 5. 前置的板级初始化操作 6. u-boot的relocation 7. 后置的板级初始化操作 1. 前言 书接上文(u-boot启动流程分析(1)_平台相关部分),本文介绍u-boot启动流程中和具体版型(board)有关的部分,也即board_init_f/board_i

Cocos2d-x3.3RC0的Android编译Activity启动流程分析

本文将从引擎源代码Jni分析Cocos2d-x3.3RC0的Android Activity的启动流程,以下是具体分析. 1.引擎源代码Jni.部分Java层和C++层代码分析 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXV4aWt1b18x/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" > watermark/2/text/aHR0cDov

u-boot启动流程分析(1)_平台相关部分

转自:http://www.wowotech.net/u-boot/boot_flow_1.html 1. 前言 本文将结合u-boot的“board—>machine—>arch—>cpu”框架,介绍u-boot中平台相关部分的启动流程.并通过对启动流程的简单分析,掌握u-boot移植的基本方法. 注1:本文所使用的u-boot版本,是2016/4/23从u-boot官网(git://git.denx.de/u-boot.git)导入的一个快照,具体可参考“https://github