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的自动装配原理。

 // ConfigurationClassParser类
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
// 如果是SpringBoot项目进来的,bd其实就是前面主类封装成的 AnnotatedGenericBeanDefinition(AnnotatedBeanDefinition接口的实现类)
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
} else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
} else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
} catch (BeanDefinitionStoreException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// 加载默认的配置---》(对springboot项目来说这里就是自动装配的入口了)
processDeferredImportSelectors();
}

二、SpringBoot自动装配原理。

2.1、@SpringBootApplication注解

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

 @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}

  接着看@EnableAutoConfiguration

 @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}

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

2.2、AutoConfigurationImportSelector

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

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

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

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

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

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

  看看loadFactoryNames()方法

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

  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就能扫描到。

 <dependency>
<groupId>com.demo</groupId>
<artifactId>demo-spring-boot-starter</artifactId>
<version>0.0.1-RELEASE</version>
</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注解。如下所示:

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

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

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

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

  

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

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

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

  1. SpringBoot启动流程分析(六):IoC容器依赖注入

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  2. SpringBoot启动流程分析(一):SpringApplication类初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  3. SpringBoot启动流程分析(二):SpringApplication的run方法

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  4. SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

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

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  6. SpringBoot启动流程分析原理(一)

    我们都知道SpringBoot自问世以来,一直有一个响亮的口号"约定优于配置",其实一种按约定编程的软件设计范式,目的在于减少软件开发人员在工作中的各种繁琐的配置,我们都知道传统的 ...

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

    前言 Spring翻译为中文是“春天”,的确,在某段时间内,它给Java开发人员带来过春天,但是随着我们项目规模的扩大,Spring需要配置的地方就越来越多,夸张点说,“配置两小时,Coding五分钟 ...

  8. springboot启动流程(五)创建ApplicationContext

    所有文章 https://www.cnblogs.com/lay2017/p/11478237.html 正文 springboot在启动过程中将会根据当前应用的类型创建对应的ApplicationC ...

  9. SpringBoot启动流程分析

    前景提示 @ComponentScan  的处理都放在org.springframework.context.annotation.ConfigurationClassParser#doProcess ...

随机推荐

  1. Error:scalac: Error: org.jetbrains.jps.incremental.scala.remote.ServerException

    Error:scalac: Error: org.jetbrains.jps.incremental.scala.remote.ServerException reason:JDK与Scala的版本不 ...

  2. 腾讯消消乐 (状态压缩DP)

    腾讯消消乐 题意 给出长度为 n 的序列,每次可以选择删除序列的一个连续区间,要求这一段区间内所有数最大公约数不小于 k ,删除后剩下的序列仍然构成连续序列. 定义 f(i) 为进行 i 次操作将整个 ...

  3. [Contest20180418]数学竞赛

    题意:初始时$x=0$(长度),当$x$为长度时,你可以把$x$变成$\sin^{-1}x,\cos^{-1}x,\tan^{-1}x$之一($x$变为角度),若$x$为角度,你可以把$x$变成$\s ...

  4. [POI2014]Freight

    题目大意: 有两个城镇$A$和$B$,有$n(n\le10^6)$辆车从$A$地出发前往$B$再返回$A$地.两地之间的行驶时间为$s(s\le10^9)$,每辆车从$A$地出发的最早时间是$t_i( ...

  5. [51Nod1487]占领资源

    题目大意:​ 有一个$n\times m(x,m\leq 100)$的网格图,每个格子有一个权值$w_{i,j}(1\leq w_{i,j}\leq 9)$.你可以在图中选两个格子,每个格子$(x,y ...

  6. javascript与java的不同之处

    javascript与java的不同之处   虽然很像,但不是一种语言. 二者的区别体现在: 首先,它们是两个公司开发的不同的两个产品,Java是SUN公司推出的新一代面向对象的程序设计语言,特别适合 ...

  7. 解决NVidia显卡最大化和最小化窗口时的卡顿问题

    最近因为做一个3D的项目,换上了一块Nvidia的显卡.然而,在使用的过程中,发现最大化和最小化窗口时的动画有卡顿现象,并且一般出现一次后便不会再出现, 可是等一会又会出现同样问题. 我以前使用i7的 ...

  8. alter table锁表,MySQL出现Waiting for table metadata lock的场景浅析及解决方案

    在修改/增加表字段的时候,发现很慢, show processlist; 时, Waiting for table metadata lock 能一直锁很久. 官网的一段话,可以理解下 http:// ...

  9. ASIHTTPRequest框架使用总结系列之阿堂教程1(安装配置篇

    在前年,阿堂在<IOS开发系列之阿堂教程:玩转IPhone客户端和Web服务端交互(客户端)实践>一文中,对于ASIHTTPRequest框架有过一些介简单绍,具体链接地址见http:// ...

  10. andriod 剪贴板操作

    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=&quo ...