SpringBoot系列文章简介

SpringBoot源码阅读辅助篇:

  Spring IoC容器与应用上下文的设计与实现 <https://www.cnblogs.com/hello-shf/p/11006750.html>

SpringBoot启动流程源码分析:

* SpringBoot启动流程分析(一):SpringApplication类初始化过程
<https://www.cnblogs.com/hello-shf/p/10976646.html>
* SpringBoot启动流程分析(二):SpringApplication的run方法
<https://www.cnblogs.com/hello-shf/p/10992377.html>
* SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法
<https://www.cnblogs.com/hello-shf/p/11018403.html>
* SpringBoot启动流程分析(四):IoC容器的初始化过程
<https://www.cnblogs.com/hello-shf/p/11051476.html>
* SpringBoot启动流程分析(五):SpringBoot自动装配原理实现
<https://www.cnblogs.com/hello-shf/p/11057861.html>
* SpringBoot启动流程分析(六):IoC容器依赖注入
<https://www.cnblogs.com/hello-shf/p/11060546.html>
笔者注释版Spring Framework与SpringBoot源码git传送门:请不要吝啬小星星

* spring-framework-5.0.8.RELEASE
<https://github.com/hello-shf/spring-framework-5.0.8.RELEASE.git>
* SpringBoot-2.0.4.RELEASE
<https://github.com/hello-shf/spring-boot-build.git>
自定义Starter:

  SpringBoot应用篇(一):自定义starter
<https://www.cnblogs.com/hello-shf/p/10864977.html>

一、前言


  上一篇文章,通过分析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容器的初始化过程就结束了。

 

  

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

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

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信